VOID NTAPI SignalMediaChanged(PDEVICE_OBJECT DeviceObject, PIRP Irp) /* * FUNCTION: Process an IRP when the media has changed, and possibly notify the user * ARGUMENTS: * DeviceObject: DeviceObject associated with the IRP * Irp: IRP that we're failing due to change * NOTES: * - This procedure is documented in the DDK by "Notifying the File System of Possible Media Changes", * "IoSetHardErrorOrVerifyDevice", and by "Responding to Check-Verify Requests from the File System". * - Callable at <= DISPATCH_LEVEL */ { PDRIVE_INFO DriveInfo = DeviceObject->DeviceExtension; TRACE_(FLOPPY, "SignalMediaChanged called\n"); DriveInfo->DiskChangeCount++; /* If volume is not mounted, do NOT set verify and return STATUS_IO_DEVICE_ERROR */ if(!(DeviceObject->Vpb->Flags & VPB_MOUNTED)) { Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR; Irp->IoStatus.Information = 0; return; } /* Notify the filesystem that it will need to verify the volume */ DeviceObject->Flags |= DO_VERIFY_VOLUME; Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED; Irp->IoStatus.Information = 0; /* * If this is a user-based, threaded request, let the IO manager know to pop up a box asking * the user to supply the correct media, but only if the error (which we just picked out above) * is deemed by the IO manager to be "user induced". The reason we don't just unconditionally * call IoSetHardError... is because MS might change the definition of "user induced" some day, * and we don't want to have to remember to re-code this. */ if(Irp->Tail.Overlay.Thread && IoIsErrorUserInduced(Irp->IoStatus.Status)) IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); }
VOID CdVerifyVcb ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb ) /*++ Routine Description: This routine checks that the current Vcb is valid and currently mounted on the device. It will raise on an error condition. We check whether the volume needs verification and the current state of the Vcb. Arguments: Vcb - This is the volume to verify. Return Value: None --*/ { NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK Iosb; ULONG MediaChangeCount = 0; BOOLEAN ForceVerify = FALSE; BOOLEAN DevMarkedForVerify; //KIRQL SavedIrql; /* ReactOS Change: GCC Unused variable */ PAGED_CODE(); // // Fail immediately if the volume is in the progress of being dismounted // or has been marked invalid. // if ((Vcb->VcbCondition == VcbInvalid) || ((Vcb->VcbCondition == VcbDismountInProgress) && (IrpContext->MajorFunction != IRP_MJ_CREATE))) { CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); } if (FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA )) { // // Capture the real device verify state. // DevMarkedForVerify = CdRealDevNeedsVerify( Vcb->Vpb->RealDevice); // // If the media is removable and the verify volume flag in the // device object is not set then we want to ping the device // to see if it needs to be verified. // if (Vcb->VcbCondition != VcbMountInProgress) { Status = CdPerformDevIoCtrl( IrpContext, IOCTL_CDROM_CHECK_VERIFY, Vcb->TargetDeviceObject, &MediaChangeCount, sizeof(ULONG), FALSE, FALSE, &Iosb ); if (Iosb.Information != sizeof(ULONG)) { // // Be safe about the count in case the driver didn't fill it in // MediaChangeCount = 0; } // // There are four cases when we want to do a verify. These are the // first three. // // 1. We are mounted, and the device has become empty // 2. The device has returned verify required (=> DO_VERIFY_VOL flag is // set, but could be due to hardware condition) // 3. Media change count doesn't match the one in the Vcb // if (((Vcb->VcbCondition == VcbMounted) && CdIsRawDevice( IrpContext, Status )) || (Status == STATUS_VERIFY_REQUIRED) || (NT_SUCCESS(Status) && (Vcb->MediaChangeCount != MediaChangeCount))) { // // If we are currently the volume on the device then it is our // responsibility to set the verify flag. If we're not on the device, // then we shouldn't touch the flag. // if (!FlagOn( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE) && !DevMarkedForVerify) { DevMarkedForVerify = CdMarkDevForVerifyIfVcbMounted( Vcb); } ForceVerify = TRUE; // // NOTE that we no longer update the media change count here. We // do so only when we've actually completed a verify at a particular // change count value. // } } // // This is the 4th verify case. // // We ALWAYS force CREATE requests on unmounted volumes through the // verify path. These requests could have been in limbo between // IoCheckMountedVpb and us when a verify/mount took place and caused // a completely different fs/volume to be mounted. In this case the // checks above may not have caught the condition, since we may already // have verified (wrong volume) and decided that we have nothing to do. // We want the requests to be re routed to the currently mounted volume, // since they were directed at the 'drive', not our volume. // if (NT_SUCCESS( Status) && !ForceVerify && (IrpContext->MajorFunction == IRP_MJ_CREATE)) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp); ForceVerify = (IrpSp->FileObject->RelatedFileObject == NULL) && ((Vcb->VcbCondition == VcbDismountInProgress) || (Vcb->VcbCondition == VcbNotMounted)); // // Note that we don't touch the device verify flag here. It required // it would have been caught and set by the first set of checks. // } // // Raise the verify / error if neccessary. // if (ForceVerify || !NT_SUCCESS( Status)) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); CdRaiseStatus( IrpContext, ForceVerify ? STATUS_VERIFY_REQUIRED : Status); } } // // Based on the condition of the Vcb we'll either return to our // caller or raise an error condition // switch (Vcb->VcbCondition) { case VcbNotMounted: IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); break; case VcbInvalid: case VcbDismountInProgress : CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); break; /* ReactOS Change: GCC "enumeration value not handled in switch" */ default: break; } }
BOOLEAN CdVerifyFcbOperation ( IN PIRP_CONTEXT IrpContext OPTIONAL, IN PFCB Fcb ) /*++ Routine Description: This routine is called to verify that the state of the Fcb is valid to allow the current operation to continue. We use the state of the Vcb, target device and type of operation to determine this. Arguments: IrpContext - IrpContext for the request. If not present then we were called from the fast IO path. Fcb - Fcb to perform the request on. Return Value: BOOLEAN - TRUE if the request can continue, FALSE otherwise. --*/ { //NTSTATUS Status = STATUS_SUCCESS; /* ReactOS Change: GCC Unused variable */ PVCB Vcb = Fcb->Vcb; PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice; PIRP Irp; PAGED_CODE(); // // Check that the fileobject has not been cleaned up. // if ( ARGUMENT_PRESENT( IrpContext )) { PFILE_OBJECT FileObject; Irp = IrpContext->Irp; FileObject = IoGetCurrentIrpStackLocation( Irp)->FileObject; if ( FileObject && FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE)) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); // // Following FAT, we allow certain operations even on cleaned up // file objects. Everything else, we fail. // if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) || (IrpSp->MajorFunction == IRP_MJ_CLOSE ) || (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) || ( (IrpSp->MajorFunction == IRP_MJ_READ) && FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) { NOTHING; } else { CdRaiseStatus( IrpContext, STATUS_FILE_CLOSED ); } } } // // Fail immediately if the volume is in the progress of being dismounted // or has been marked invalid. // if ((Vcb->VcbCondition == VcbInvalid) || (Vcb->VcbCondition == VcbDismountInProgress)) { if (ARGUMENT_PRESENT( IrpContext )) { CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); } return FALSE; } // // Always fail if the volume needs to be verified. // if (CdRealDevNeedsVerify( RealDevice)) { if (ARGUMENT_PRESENT( IrpContext )) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice ); CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); } return FALSE; // // // All operations are allowed on mounted. // } else if ((Vcb->VcbCondition == VcbMounted) || (Vcb->VcbCondition == VcbMountInProgress)) { return TRUE; // // Fail all requests for fast Io on other Vcb conditions. // } else if (!ARGUMENT_PRESENT( IrpContext )) { return FALSE; // // The remaining case is VcbNotMounted. // Mark the device to be verified and raise WRONG_VOLUME. // } else if (Vcb->VcbCondition == VcbNotMounted) { if (ARGUMENT_PRESENT( IrpContext )) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice ); CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); } return FALSE; } return TRUE; }
NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context) { PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context; PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(pkt->OriginalIrp); BOOLEAN packetDone = FALSE; /* * Put all the assertions and spew in here so we don't have to look at them. */ DBGCHECKRETURNEDPKT(pkt); if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){ fdoData->LoggedTURFailureSinceLastIO = FALSE; /* * The port driver should not have allocated a sense buffer * if the SRB succeeded. */ ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)); /* * Add this packet's transferred length to the original IRP's. */ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, (LONG)pkt->Srb.DataTransferLength); if (pkt->InLowMemRetry){ packetDone = StepLowMemRetry(pkt); } else { packetDone = TRUE; } } else { /* * The packet failed. We may retry it if possible. */ BOOLEAN shouldRetry; /* * Make sure IRP status matches SRB error status (since we propagate it). */ if (NT_SUCCESS(Irp->IoStatus.Status)){ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } /* * Interpret the SRB error (to a meaningful IRP status) * and determine if we should retry this packet. * This call looks at the returned SENSE info to figure out what to do. */ shouldRetry = InterpretTransferPacketError(pkt); /* * Sometimes the port driver can allocates a new 'sense' buffer * to report transfer errors, e.g. when the default sense buffer * is too small. If so, it is up to us to free it. * Now that we're done interpreting the sense info, free it if appropriate. */ if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) { DBGTRACE(ClassDebugSenseInfo, ("Freeing port-allocated sense buffer for pkt %ph.", pkt)); FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb); pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData; pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); } /* * If the SRB queue is locked-up, release it. * Do this after calling the error handler. */ if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){ ClassReleaseQueue(pkt->Fdo); } if (shouldRetry && (pkt->NumRetries > 0)){ packetDone = RetryTransferPacket(pkt); } else { packetDone = TRUE; } } /* * If the packet is completed, put it back in the free list. * If it is the last packet servicing the original request, complete the original irp. */ if (packetDone){ LONG numPacketsRemaining; PIRP deferredIrp; PDEVICE_OBJECT Fdo = pkt->Fdo; UCHAR uniqueAddr; /* * In case a remove is pending, bump the lock count so we don't get freed * right after we complete the original irp. */ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr); /* * The original IRP should get an error code * if any one of the packets failed. */ if (!NT_SUCCESS(Irp->IoStatus.Status)){ pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; /* * If the original I/O originated in user space (i.e. it is thread-queued), * and the error is user-correctable (e.g. media is missing, for removable media), * alert the user. * Since this is only one of possibly several packets completing for the original IRP, * we may do this more than once for a single request. That's ok; this allows * us to test each returned status with IoIsErrorUserInduced(). */ if (IoIsErrorUserInduced(Irp->IoStatus.Status) && pkt->CompleteOriginalIrpWhenLastPacketCompletes && pkt->OriginalIrp->Tail.Overlay.Thread){ IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, pkt->Fdo); } } /* * We use a field in the original IRP to count * down the transfer pieces as they complete. */ numPacketsRemaining = InterlockedDecrement( (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]); if (numPacketsRemaining > 0){ /* * More transfer pieces remain for the original request. * Wait for them to complete before completing the original irp. */ } else { /* * All the transfer pieces are done. * Complete the original irp if appropriate. */ ASSERT(numPacketsRemaining == 0); if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){ ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == origCurrentSp->Parameters.Read.Length); ClasspPerfIncrementSuccessfulIo(fdoExt); } ClassReleaseRemoveLock(pkt->Fdo, pkt->OriginalIrp); ClassCompleteRequest(pkt->Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT); /* * We may have been called by one of the class drivers (e.g. cdrom) * via the legacy API ClassSplitRequest. * This is the only case for which the packet engine is called for an FDO * with a StartIo routine; in that case, we have to call IoStartNextPacket * now that the original irp has been completed. */ if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) { if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){ DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)")); } else { KIRQL oldIrql; KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(pkt->Fdo, FALSE); KeLowerIrql(oldIrql); } } } } /* * If the packet was synchronous, write the final * result back to the issuer's status buffer and * signal his event. */ if (pkt->SyncEventPtr){ KeSetEvent(pkt->SyncEventPtr, 0, FALSE); pkt->SyncEventPtr = NULL; } /* * Free the completed packet. */ pkt->OriginalIrp = NULL; pkt->InLowMemRetry = FALSE; EnqueueFreeTransferPacket(pkt->Fdo, pkt); /* * Now that we have freed some resources, * try again to send one of the previously deferred irps. */ deferredIrp = DequeueDeferredClientIrp(fdoData); if (deferredIrp){ DBGWARN(("... retrying deferred irp %xh.", deferredIrp)); ServiceTransferRequest(pkt->Fdo, deferredIrp); } ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr); } return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CdPerformVerify ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN PDEVICE_OBJECT DeviceToVerify ) /*++ Routine Description: This routines performs an IoVerifyVolume operation and takes the appropriate action. If the verify is successful then we send the originating Irp off to an Ex Worker Thread. This routine is called from the exception handler. No file system resources are held when this routine is called. Arguments: Irp - The irp to send off after all is well and done. Device - The real device needing verification. Return Value: None. --*/ { PVCB Vcb; NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); // // Check if this Irp has a status of Verify required and if it does // then call the I/O system to do a verify. // // Skip the IoVerifyVolume if this is a mount or verify request // itself. Trying a recursive mount will cause a deadlock with // the DeviceObject->DeviceLock. // if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) { return CdFsdPostRequest( IrpContext, Irp ); } // // Extract a pointer to the Vcb from the VolumeDeviceObject. // Note that since we have specifically excluded mount, // requests, we know that IrpSp->DeviceObject is indeed a // volume device object. // IrpSp = IoGetCurrentIrpStackLocation( Irp ); Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject, VOLUME_DEVICE_OBJECT, DeviceObject )->Vcb; try { // // Send down the verify FSCTL. Note that this is sent to the // currently mounted volume, which may not be this one. // // We will allow Raw to mount this volume if we were doing a // an absolute DASD open. // Status = IoVerifyVolume( DeviceToVerify, CdOperationIsDasdOpen( IrpContext)); // // Acquire the Vcb so we're working with a stable VcbCondition. // CdAcquireVcbShared( IrpContext, Vcb, FALSE); // // If the verify operation completed it will return // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. // // If CdVerifyVolume encountered an error during // processing, it will return that error. If we got // STATUS_WRONG_VOLUME from the verify, and our volume // is now mounted, commute the status to STATUS_SUCCESS. // if ((Status == STATUS_WRONG_VOLUME) && (Vcb->VcbCondition == VcbMounted)) { Status = STATUS_SUCCESS; } else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbMounted)) { // // If the verify succeeded, but our volume is not mounted, // then some other volume is on the device. // Status = STATUS_WRONG_VOLUME; } // // Do a quick unprotected check here. The routine will do // a safe check. After here we can release the resource. // Note that if the volume really went away, we will be taking // the Reparse path. // // // If the device might need to go away then call our dismount routine. // if (((Vcb->VcbCondition == VcbNotMounted) || (Vcb->VcbCondition == VcbInvalid) || (Vcb->VcbCondition == VcbDismountInProgress)) && (Vcb->VcbReference <= CDFS_RESIDUAL_REFERENCE)) { CdReleaseVcb( IrpContext, Vcb); CdAcquireCdData( IrpContext ); CdCheckForDismount( IrpContext, Vcb, FALSE ); CdReleaseCdData( IrpContext ); } else { CdReleaseVcb( IrpContext, Vcb); } // // If this is a create and the verify succeeded then complete the // request with a REPARSE status. // if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && (IrpSp->FileObject->RelatedFileObject == NULL) && ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) { Irp->IoStatus.Information = IO_REMOUNT; CdCompleteRequest( IrpContext, Irp, STATUS_REPARSE ); Status = STATUS_REPARSE; Irp = NULL; IrpContext = NULL; // // If there is still an error to process then call the Io system // for a popup. // } else if ((Irp != NULL) && !NT_SUCCESS( Status )) { // // Fill in the device object if required. // if (IoIsErrorUserInduced( Status ) ) { IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify ); } CdNormalizeAndRaiseStatus( IrpContext, Status ); } // // If there is still an Irp, send it off to an Ex Worker thread. // if (IrpContext != NULL) { Status = CdFsdPostRequest( IrpContext, Irp ); } } except(CdExceptionFilter( IrpContext, GetExceptionInformation() )) { // // We had some trouble trying to perform the verify or raised // an error ourselves. So we'll abort the I/O request with // the error status that we get back from the execption code. // Status = CdProcessException( IrpContext, Irp, GetExceptionCode() ); } return Status; }
/* Routine Description: This routines performs an IoVerifyVolume operation and takes the appropriate action. If the verify is successful then we send the originating Irp off to an Ex Worker Thread. This routine is called from the exception handler. No file system resources are held when this routine is called. Arguments: Irp - The irp to send off after all is well and done. Device - The real device needing verification. */ NTSTATUS UDFPerformVerify( IN PtrUDFIrpContext IrpContext, IN PIRP Irp, IN PDEVICE_OBJECT DeviceToVerify ) { PVCB Vcb; NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; KdPrint(("UDFPerformVerify:\n")); if(!IrpContext) return STATUS_INVALID_PARAMETER; if(!Irp) return STATUS_INVALID_PARAMETER; // Check if this Irp has a status of Verify required and if it does // then call the I/O system to do a verify. // // Skip the IoVerifyVolume if this is a mount or verify request // itself. Trying a recursive mount will cause a deadlock with // the DeviceObject->DeviceLock. if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) { return UDFPostRequest(IrpContext, Irp); } // Extract a pointer to the Vcb from the VolumeDeviceObject. // Note that since we have specifically excluded mount, // requests, we know that IrpSp->DeviceObject is indeed a // volume device object. IrpSp = IoGetCurrentIrpStackLocation(Irp); Vcb = (PVCB)IrpSp->DeviceObject->DeviceExtension; KdPrint(("UDFPerformVerify: check\n")); // Check if the volume still thinks it needs to be verified, // if it doesn't then we can skip doing a verify because someone // else beat us to it. _SEH2_TRY { if (DeviceToVerify->Flags & DO_VERIFY_VOLUME) { // If the IopMount in IoVerifyVolume did something, and // this is an absolute open, force a reparse. RC = IoVerifyVolume( DeviceToVerify, FALSE ); // Bug? /* if (UDFIsRawDevice(RC)) { RC = STATUS_WRONG_VOLUME; }*/ // If the verify operation completed it will return // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. if (RC == STATUS_SUCCESS) { IrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_EXCEPTION; } // If UDFVerifyVolume encountered an error during // processing, it will return that error. If we got // STATUS_WRONG_VOLUME from the verify, and our volume // is now mounted, commute the status to STATUS_SUCCESS. if ((RC == STATUS_WRONG_VOLUME) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) { RC = STATUS_SUCCESS; } // Do a quick unprotected check here. The routine will do // a safe check. After here we can release the resource. // Note that if the volume really went away, we will be taking // the Reparse path. // If the device might need to go away then call our dismount routine. if ( (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) || (Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) && (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE) ) { KdPrint(("UDFPerformVerify: UDFCheckForDismount\n")); UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); UDFCheckForDismount( IrpContext, Vcb, FALSE ); UDFReleaseResource(&(UDFGlobalData.GlobalDataResource)); } // If this is a create and the verify succeeded then complete the // request with a REPARSE status. if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && (IrpSp->FileObject->RelatedFileObject == NULL) && ((RC == STATUS_SUCCESS) || (RC == STATUS_WRONG_VOLUME)) ) { KdPrint(("UDFPerformVerify: IO_REMOUNT\n")); Irp->IoStatus.Information = IO_REMOUNT; Irp->IoStatus.Status = STATUS_REPARSE; IoCompleteRequest(Irp,IO_DISK_INCREMENT); UDFReleaseIrpContext(IrpContext); RC = STATUS_REPARSE; Irp = NULL; IrpContext = NULL; // If there is still an error to process then call the Io system // for a popup. } else if ((Irp != NULL) && !NT_SUCCESS( RC )) { KdPrint(("UDFPerformVerify: check IoIsErrorUserInduced\n")); // Fill in the device object if required. if (IoIsErrorUserInduced( RC ) ) { IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify ); } KdPrint(("UDFPerformVerify: UDFNormalizeAndRaiseStatus\n")); UDFNormalizeAndRaiseStatus( IrpContext, RC ); } } // If there is still an Irp, send it off to an Ex Worker thread. if (IrpContext != NULL) { RC = UDFPostRequest( IrpContext, Irp ); } } _SEH2_EXCEPT(UDFExceptionFilter( IrpContext, _SEH2_GetExceptionInformation())) { // We had some trouble trying to perform the verify or raised // an error ourselves. So we'll abort the I/O request with // the error status that we get back from the execption code. RC = UDFExceptionHandler( IrpContext, Irp); } _SEH2_END; KdPrint(("UDFPerformVerify: RC = %x\n", RC)); return RC; } // end UDFPerformVerify()
/*++//////////////////////////////////////////////////////////////////////////// ClassIoCompleteAssociated() Routine Description: This routine executes when the port driver has completed a request. It looks at the SRB status in the completing SRB and if not success it checks for valid request sense buffer information. If valid, the info is used to update status with more precise message of type of error. This routine deallocates the SRB. This routine is used for requests which were build by split request. After it has processed the request it decrements the Irp count in the master Irp. If the count goes to zero then the master Irp is completed. Arguments: Fdo - Supplies the functional device object which represents the target. Irp - Supplies the Irp which has completed. Context - Supplies a pointer to the SRB. Return Value: NT status --*/ NTSTATUS NTAPI ClassIoCompleteAssociated( IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN PVOID Context ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = Context; PIRP originalIrp = Irp->AssociatedIrp.MasterIrp; LONG irpCount; NTSTATUS status; BOOLEAN retry; DBGWARN(("ClassIoCompleteAssociated is OBSOLETE !")); // // Check SRB status for success of completing request. // if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { ULONG retryInterval; DebugPrint((2,"ClassIoCompleteAssociated: IRP %p, SRB %p", Irp, srb)); // // Release the queue if it is frozen. // if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { ClassReleaseQueue(Fdo); } retry = ClassInterpretSenseInfo( Fdo, srb, irpStack->MajorFunction, irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4), &status, &retryInterval); // // If the status is verified required and the this request // should bypass verify required then retry the request. // if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && status == STATUS_VERIFY_REQUIRED) { status = STATUS_IO_DEVICE_ERROR; retry = TRUE; } if (retry && ((*(PCHAR*)&irpStack->Parameters.Others.Argument4)--)) { // // Retry request. If the class driver has supplied a StartIo, // call it directly for retries. // DebugPrint((1, "Retry request %p\n", Irp)); if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); } RetryRequest(Fdo, Irp, srb, TRUE, retryInterval); return STATUS_MORE_PROCESSING_REQUIRED; } } else { // // Set status for successful request. // status = STATUS_SUCCESS; } // end if (SRB_STATUS(srb->SrbStatus) ... // // Return SRB to list. // if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); } ClassFreeOrReuseSrb(fdoExtension, srb); // // Set status in completing IRP. // Irp->IoStatus.Status = status; DebugPrint((2, "ClassIoCompleteAssociated: Partial xfer IRP %p\n", Irp)); // // Get next stack location. This original request is unused // except to keep track of the completing partial IRPs so the // stack location is valid. // irpStack = IoGetNextIrpStackLocation(originalIrp); // // Update status only if error so that if any partial transfer // completes with error, then the original IRP will return with // error. If any of the asynchronous partial transfer IRPs fail, // with an error then the original IRP will return 0 bytes transfered. // This is an optimization for successful transfers. // if (!NT_SUCCESS(status)) { originalIrp->IoStatus.Status = status; originalIrp->IoStatus.Information = 0; // // Set the hard error if necessary. // if (IoIsErrorUserInduced(status)) { // // Store DeviceObject for filesystem. // IoSetHardErrorOrVerifyDevice(originalIrp, Fdo); } } // // Decrement and get the count of remaining IRPs. // irpCount = InterlockedDecrement( (PLONG)&irpStack->Parameters.Others.Argument1); DebugPrint((2, "ClassIoCompleteAssociated: Partial IRPs left %d\n", irpCount)); // // Ensure that the irpCount doesn't go negative. This was happening once // because classpnp would get confused if it ran out of resources when // splitting the request. // ASSERT(irpCount >= 0); if (irpCount == 0) { // // All partial IRPs have completed. // DebugPrint((2, "ClassIoCompleteAssociated: All partial IRPs complete %p\n", originalIrp)); if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) { // // Acquire a separate copy of the remove lock so the debugging code // works okay and we don't have to hold up the completion of this // irp until after we start the next packet(s). // KIRQL oldIrql; UCHAR uniqueAddress; ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress); ClassReleaseRemoveLock(Fdo, originalIrp); ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(Fdo, FALSE); KeLowerIrql(oldIrql); ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress); } else { // // just complete this request // ClassReleaseRemoveLock(Fdo, originalIrp); ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT); } } // // Deallocate IRP and indicate the I/O system should not attempt any more // processing. // IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } // end ClassIoCompleteAssociated()
NTSTATUS UDFVerifyVcb( IN PtrUDFIrpContext IrpContext, IN PVCB Vcb ) { NTSTATUS RC = STATUS_SUCCESS; IO_STATUS_BLOCK Iosb; ULONG MediaChangeCount = 0; BOOLEAN Nop = TRUE; BOOLEAN UnsafeIoctl = (Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL) ? TRUE : FALSE; KdPrint(("UDFVerifyVCB: Modified=%d\n", Vcb->Modified)); // Fail immediately if the volume is in the progress of being dismounted // or has been marked invalid. if (Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED) { return STATUS_FILE_INVALID; } // If the media is removable and the verify volume flag in the // device object is not set then we want to ping the device // to see if it needs to be verified if ( (Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) && !(Vcb->Vpb->RealDevice->Flags & DO_VERIFY_VOLUME) && (!(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_LOCKED) || UnsafeIoctl) ) { KdPrint(("UDFVerifyVCB: UnsafeIoctl=%d, locked=%d\n", UnsafeIoctl, (Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_LOCKED) ? 0 : 1)); Vcb->VCBFlags &= ~UDF_VCB_FLAGS_UNSAFE_IOCTL; RC = UDFTSendIOCTL( IOCTL_STORAGE_CHECK_VERIFY, Vcb, NULL,0, &MediaChangeCount,sizeof(ULONG), FALSE,&Iosb ); // Be safe about the count in case the driver didn't fill it in if (Iosb.Information != sizeof(ULONG)) MediaChangeCount = 0; KdPrint((" MediaChangeCount %d -> %d\n", Vcb->MediaChangeCount, MediaChangeCount)); // If the volume is now an empty device, or we have receieved a // bare STATUS_VERIFY_REQUIRED (various hardware conditions such // as bus resets, etc., will trigger this in the drivers), or the // media change count has moved since we last inspected the device, // then mark the volume to be verified. if ( (RC == STATUS_VERIFY_REQUIRED) || (UDFIsRawDevice(RC) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) || (NT_SUCCESS(RC) && (Vcb->MediaChangeCount != MediaChangeCount)) || UnsafeIoctl) { KdPrint((" set DO_VERIFY_VOLUME\n")); Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME; // If the volume is not mounted and we got a media change count, // update the Vcb so we do not trigger a verify again at this // count value. If the verify->mount path detects that the media // has actually changed and this Vcb is valid again, this will have // done nothing. We are already synchronized since the caller has // the Vcb. if (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) && NT_SUCCESS(RC) ) { Vcb->MediaChangeCount = MediaChangeCount; } } else if (!NT_SUCCESS(RC)) { // Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME; KdPrint((" UDFNormalizeAndRaiseStatus(%x)\n", RC)); UDFNormalizeAndRaiseStatus(IrpContext,RC); ASSERT(Nop); } } KdPrint(("UDFVerifyVCB: Modified=%d\n", Vcb->Modified)); // The Vcb may be mounted but the underlying real device may need to be verified. // If it does then we'll set the Iosb in the irp to be our real device if (Vcb->Vpb->RealDevice->Flags & DO_VERIFY_VOLUME) { KdPrint((" DO_VERIFY_VOLUME -> IoSetHardErrorOrVerifyDevice()\n")); IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); RC = STATUS_VERIFY_REQUIRED; KdPrint((" UDFRaiseStatus()\n")); UDFRaiseStatus(IrpContext, RC); ASSERT(Nop); } KdPrint(("UDFVerifyVCB: Modified=%d\n", Vcb->Modified)); if (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) { KdPrint((" !UDF_VCB_FLAGS_VOLUME_MOUNTED -> IoSetHardErrorOrVerifyDevice()\n")); Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME; IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); RC = STATUS_WRONG_VOLUME; KdPrint((" UDFRaiseStatus()\n")); UDFRaiseStatus(IrpContext, RC); // UDFRaiseStatus(IrpContext, STATUS_UNRECOGNIZED_VOLUME); ASSERT(Nop); } if ((Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) { KdPrint((" UDF_VCB_FLAGS_BEING_DISMOUNTED\n")); RC = STATUS_FILE_INVALID; UDFRaiseStatus( IrpContext, RC ); ASSERT(Nop); } KdPrint(("UDFVerifyVcb: RC = %x\n", RC)); return RC; } // end UDFVerifyVcb()
BOOLEAN CdVerifyFcbOperation ( IN PIRP_CONTEXT IrpContext OPTIONAL, IN PFCB Fcb ) /*++ Routine Description: This routine is called to verify that the state of the Fcb is valid to allow the current operation to continue. We use the state of the Vcb, target device and type of operation to determine this. Arguments: IrpContext - IrpContext for the request. If not present then we were called from the fast IO path. Fcb - Fcb to perform the request on. Return Value: BOOLEAN - TRUE if the request can continue, FALSE otherwise. --*/ { NTSTATUS Status = STATUS_SUCCESS; PVCB Vcb = Fcb->Vcb; PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice; PAGED_CODE(); // // Fail immediately if the volume is in the progress of being dismounted // or has been marked invalid. // if ((Vcb->VcbCondition == VcbInvalid) || (Vcb->VcbCondition == VcbDismountInProgress)) { if (ARGUMENT_PRESENT( IrpContext )) { CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); } return FALSE; } // // Always fail if the volume needs to be verified. // if (FlagOn( RealDevice->Flags, DO_VERIFY_VOLUME )) { if (ARGUMENT_PRESENT( IrpContext )) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice ); CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); } return FALSE; // // // All operations are allowed on mounted. // } else if ((Vcb->VcbCondition == VcbMounted) || (Vcb->VcbCondition == VcbMountInProgress)) { return TRUE; // // Fail all requests for fast Io on other Vcb conditions. // } else if (!ARGUMENT_PRESENT( IrpContext )) { return FALSE; // // The remaining case is VcbNotMounted. // Mark the device to be verified and raise WRONG_VOLUME. // } else if (Vcb->VcbCondition == VcbNotMounted) { if (ARGUMENT_PRESENT( IrpContext )) { SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME); IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice ); CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); } return FALSE; } return TRUE; }
VOID CdVerifyVcb ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb ) /*++ Routine Description: This routine checks that the current Vcb is valid and currently mounted on the device. It will raise on an error condition. We check whether the volume needs verification and the current state of the Vcb. Arguments: Vcb - This is the volume to verify. Return Value: None --*/ { NTSTATUS Status; IO_STATUS_BLOCK Iosb; ULONG MediaChangeCount = 0; PAGED_CODE(); // // Fail immediately if the volume is in the progress of being dismounted // or has been marked invalid. // if ((Vcb->VcbCondition == VcbInvalid) || (Vcb->VcbCondition == VcbDismountInProgress)) { CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); } // // If the media is removable and the verify volume flag in the // device object is not set then we want to ping the device // to see if it needs to be verified // if ((Vcb->VcbCondition != VcbMountInProgress) && FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA ) && !FlagOn( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME )) { Status = CdPerformDevIoCtrl( IrpContext, IOCTL_CDROM_CHECK_VERIFY, Vcb->TargetDeviceObject, &MediaChangeCount, sizeof(ULONG), FALSE, FALSE, &Iosb ); if (Iosb.Information != sizeof(ULONG)) { // // Be safe about the count in case the driver didn't fill it in // MediaChangeCount = 0; } // // If the volume is now an empty device, or we have receieved a // bare STATUS_VERIFY_REQUIRED (various hardware conditions such // as bus resets, etc., will trigger this in the drivers), or the // media change count has moved since we last inspected the device, // then mark the volume to be verified. // if ((Vcb->VcbCondition == VcbMounted && CdIsRawDevice( IrpContext, Status )) || (Status == STATUS_VERIFY_REQUIRED) || (NT_SUCCESS(Status) && (Vcb->MediaChangeCount != MediaChangeCount))) { SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME ); // // If the volume is not mounted and we got a media change count, // update the Vcb so we do not trigger a verify again at this // count value. If the verify->mount path detects that the media // has actually changed and this Vcb is valid again, this will have // done nothing. We are already synchronized since the caller has // the Vcb. // if ((Vcb->VcbCondition == VcbNotMounted) && NT_SUCCESS(Status)) { Vcb->MediaChangeCount = MediaChangeCount; } // // Raise the error condition otherwise. // } else if (!NT_SUCCESS( Status )) { CdNormalizeAndRaiseStatus( IrpContext, Status ); } } // // The Vcb may be mounted but the underlying real device may need to be verified. // If it does then we'll set the Iosb in the irp to be our real device // and raise Verify required // if (FlagOn( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME )) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); } // // Based on the condition of the Vcb we'll either return to our // caller or raise an error condition // switch (Vcb->VcbCondition) { case VcbNotMounted: SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME ); IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); break; case VcbInvalid: case VcbDismountInProgress : CdRaiseStatus( IrpContext, STATUS_FILE_INVALID ); break; } return; }
VOID FFSUnpinRepinnedBcbs( IN PFFS_IRP_CONTEXT IrpContext) { IO_STATUS_BLOCK RaiseIosb; PFFS_REPINNED_BCBS Repinned; BOOLEAN WriteThroughToDisk; PFILE_OBJECT FileObject = NULL; BOOLEAN ForceVerify = FALSE; ULONG i; Repinned = &IrpContext->Repinned; RaiseIosb.Status = STATUS_SUCCESS; WriteThroughToDisk = (BOOLEAN)(IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH) || IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_FLOPPY)); while (Repinned != NULL) { for (i = 0; i < FFS_REPINNED_BCBS_ARRAY_SIZE; i += 1) { if (Repinned->Bcb[i] != NULL) { IO_STATUS_BLOCK Iosb; ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_FLOPPY)) { FileObject = CcGetFileObjectFromBcb(Repinned->Bcb[i]); } ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); CcUnpinRepinnedBcb(Repinned->Bcb[i], WriteThroughToDisk, &Iosb); ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); if (!NT_SUCCESS(Iosb.Status)) { if (RaiseIosb.Status == STATUS_SUCCESS) { RaiseIosb = Iosb; } if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_FLOPPY) && (IrpContext->MajorFunction != IRP_MJ_CLEANUP) && (IrpContext->MajorFunction != IRP_MJ_FLUSH_BUFFERS) && (IrpContext->MajorFunction != IRP_MJ_SET_INFORMATION)) { CcPurgeCacheSection(FileObject->SectionObjectPointer, NULL, 0, FALSE); ForceVerify = TRUE; } } Repinned->Bcb[i] = NULL; } else { break; } } if (Repinned != &IrpContext->Repinned) { PFFS_REPINNED_BCBS Saved; Saved = Repinned->Next; ExFreePool(Repinned); Repinned = Saved; } else { Repinned = Repinned->Next; IrpContext->Repinned.Next = NULL; } } if (!NT_SUCCESS(RaiseIosb.Status)) { FFSBreakPoint(); if (ForceVerify && FileObject) { SetFlag(FileObject->DeviceObject->Flags, DO_VERIFY_VOLUME); IoSetHardErrorOrVerifyDevice(IrpContext->Irp, FileObject->DeviceObject); } IrpContext->Irp->IoStatus = RaiseIosb; FFSNormalizeAndRaiseStatus(IrpContext, RaiseIosb.Status); } return; }
NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context) { PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context; PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension; PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; BOOLEAN packetDone = FALSE; BOOLEAN idleRequest = FALSE; /* * Put all the assertions and spew in here so we don't have to look at them. */ DBGLOGRETURNPACKET(pkt); DBGCHECKRETURNEDPKT(pkt); HISTORYLOGRETURNEDPACKET(pkt); if (fdoData->IdlePrioritySupported == TRUE) { idleRequest = ClasspIsIdleRequest(pkt->OriginalIrp); if (idleRequest) { InterlockedDecrement(&fdoData->ActiveIdleIoCount); ASSERT(fdoData->ActiveIdleIoCount >= 0); } else { InterlockedDecrement(&fdoData->ActiveIoCount); ASSERT(fdoData->ActiveIoCount >= 0); KeQuerySystemTime(&fdoData->LastIoTime); fdoData->IdleTicks = 0; } } // // If partial MDL was used, unmap the pages. When the packet is retried, the // MDL will be recreated. If the packet is done, the MDL will be ready to be reused. // if (pkt->UsePartialMdl) { MmPrepareMdlForReuse(pkt->PartialMdl); } if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS) { fdoData->LoggedTURFailureSinceLastIO = FALSE; /* * The port driver should not have allocated a sense buffer * if the SRB succeeded. */ ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)); /* * Add this packet's transferred length to the original IRP's. */ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, (LONG)pkt->Srb.DataTransferLength); if ((pkt->InLowMemRetry) || (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) { packetDone = StepLowMemRetry(pkt); } else { packetDone = TRUE; } } else { /* * The packet failed. We may retry it if possible. */ BOOLEAN shouldRetry; /* * Make sure IRP status matches SRB error status (since we propagate it). */ if (NT_SUCCESS(Irp->IoStatus.Status)){ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } /* * The packet failed. * So when sending the packet down we either saw either an error or STATUS_PENDING, * and so we returned STATUS_PENDING for the original IRP. * So now we must mark the original irp pending to match that, _regardless_ of * whether we actually switch threads here by retrying. * (We also have to mark the irp pending if the lower driver marked the irp pending; * that is dealt with farther down). */ if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ IoMarkIrpPending(pkt->OriginalIrp); } /* * Interpret the SRB error (to a meaningful IRP status) * and determine if we should retry this packet. * This call looks at the returned SENSE info to figure out what to do. */ shouldRetry = InterpretTransferPacketError(pkt); /* * Sometimes the port driver can allocates a new 'sense' buffer * to report transfer errors, e.g. when the default sense buffer * is too small. If so, it is up to us to free it. * Now that we're done interpreting the sense info, free it if appropriate. * Then clear the sense buffer so it doesn't pollute future errors returned in this packet. */ if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) { TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt)); FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb); pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData; pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA); } else { ASSERT(pkt->Srb.SenseInfoBuffer == &pkt->SrbErrorSenseData); ASSERT(pkt->Srb.SenseInfoBufferLength <= sizeof(SENSE_DATA)); } RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(SENSE_DATA)); /* * If the SRB queue is locked-up, release it. * Do this after calling the error handler. */ if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){ ClassReleaseQueue(pkt->Fdo); } if (NT_SUCCESS(Irp->IoStatus.Status)){ /* * The error was recovered above in the InterpretTransferPacketError() call. */ ASSERT(!shouldRetry); /* * In the case of a recovered error, * add the transfer length to the original Irp as we would in the success case. */ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information, (LONG)pkt->Srb.DataTransferLength); if ((pkt->InLowMemRetry) || (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) { packetDone = StepLowMemRetry(pkt); } else { packetDone = TRUE; } } else { if (shouldRetry && (pkt->NumRetries > 0)){ packetDone = RetryTransferPacket(pkt); } else if (shouldRetry && (pkt->RetryHistory != NULL)){ // don't limit retries if class driver has custom interpretation routines packetDone = RetryTransferPacket(pkt); } else { packetDone = TRUE; } } } /* * If the packet is completed, put it back in the free list. * If it is the last packet servicing the original request, complete the original irp. */ if (packetDone){ LONG numPacketsRemaining; PIRP deferredIrp; PDEVICE_OBJECT Fdo = pkt->Fdo; UCHAR uniqueAddr; /* * In case a remove is pending, bump the lock count so we don't get freed * right after we complete the original irp. */ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr); /* * The original IRP should get an error code * if any one of the packets failed. */ if (!NT_SUCCESS(Irp->IoStatus.Status)){ pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status; /* * If the original I/O originated in user space (i.e. it is thread-queued), * and the error is user-correctable (e.g. media is missing, for removable media), * alert the user. * Since this is only one of possibly several packets completing for the original IRP, * we may do this more than once for a single request. That's ok; this allows * us to test each returned status with IoIsErrorUserInduced(). */ if (IoIsErrorUserInduced(Irp->IoStatus.Status) && pkt->CompleteOriginalIrpWhenLastPacketCompletes && pkt->OriginalIrp->Tail.Overlay.Thread){ IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, Fdo); } } /* * We use a field in the original IRP to count * down the transfer pieces as they complete. */ numPacketsRemaining = InterlockedDecrement( (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]); if (numPacketsRemaining > 0){ /* * More transfer pieces remain for the original request. * Wait for them to complete before completing the original irp. */ } else { /* * All the transfer pieces are done. * Complete the original irp if appropriate. */ ASSERT(numPacketsRemaining == 0); if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){ IO_PAGING_PRIORITY priority = (TEST_FLAG(pkt->OriginalIrp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(pkt->OriginalIrp) : IoPagingPriorityInvalid; KIRQL oldIrql; if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){ ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == IoGetCurrentIrpStackLocation(pkt->OriginalIrp)->Parameters.Read.Length); ClasspPerfIncrementSuccessfulIo(fdoExt); } ClassReleaseRemoveLock(Fdo, pkt->OriginalIrp); /* * We submitted all the downward irps, including this last one, on the thread * that the OriginalIrp came in on. So the OriginalIrp is completing on a * different thread iff this last downward irp is completing on a different thread. * If BlkCache is loaded, for example, it will often complete * requests out of the cache on the same thread, therefore not marking the downward * irp pending and not requiring us to do so here. If the downward request is completing * on the same thread, then by not marking the OriginalIrp pending we can save an APC * and get extra perf benefit out of BlkCache. * Note that if the packet ever cycled due to retry or LowMemRetry, * we set the pending bit in those codepaths. */ if (pkt->Irp->PendingReturned){ IoMarkIrpPending(pkt->OriginalIrp); } ClassCompleteRequest(Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT); // // Drop the count only after completing the request, to give // Mm some amount of time to issue its next critical request // if (priority == IoPagingPriorityHigh) { KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo) { fdoData->MaxInterleavedNormalIo = 0; } else { fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo; } fdoData->NumHighPriorityPagingIo--; if (fdoData->NumHighPriorityPagingIo == 0) { LARGE_INTEGER period; // // Exiting throttle mode // KeQuerySystemTime(&fdoData->ThrottleStopTime); period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart; fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart); } KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); } if (idleRequest) { ClasspCompleteIdleRequest(fdoExt); } /* * We may have been called by one of the class drivers (e.g. cdrom) * via the legacy API ClassSplitRequest. * This is the only case for which the packet engine is called for an FDO * with a StartIo routine; in that case, we have to call IoStartNextPacket * now that the original irp has been completed. */ if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) { if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (??)")); } else { KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(Fdo, TRUE); // yes, some IO is now cancellable KeLowerIrql(oldIrql); } } } } /* * If the packet was synchronous, write the final result back to the issuer's status buffer * and signal his event. */ if (pkt->SyncEventPtr){ KeSetEvent(pkt->SyncEventPtr, 0, FALSE); pkt->SyncEventPtr = NULL; } /* * Free the completed packet. */ pkt->UsePartialMdl = FALSE; // pkt->OriginalIrp = NULL; pkt->InLowMemRetry = FALSE; EnqueueFreeTransferPacket(Fdo, pkt); /* * Now that we have freed some resources, * try again to send one of the previously deferred irps. */ deferredIrp = DequeueDeferredClientIrp(fdoData); if (deferredIrp){ ServiceTransferRequest(Fdo, deferredIrp); } ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr); } return STATUS_MORE_PROCESSING_REQUIRED; }