The following guidelines briefly discuss how to avoid common programming errors in dispatch routines for file system filter drivers.
Note: For information about which types of IRPs are used in paging I/O, see Dispatch Routine IRQL and Thread Context.
Dispatch routines in the paging I/O path should never call IoCallDriver at any IRQL above APC_LEVEL. If a dispatch routine raises IRQL, it must lower it before calling IoCallDriver.
Dispatch routines in the paging path, such as read and write, cannot safely call any kernel-mode routines that require callers to be running at IRQL PASSIVE_LEVEL.
Dispatch routines that are in the paging file I/O path cannot safely call any kernel-mode routines that require a caller to be running at IRQL < DISPATCH_LEVEL.
Dispatch routines that are not in the paging I/O path should never call IoCallDriver at any IRQL above PASSIVE_LEVEL. If a dispatch routine raises IRQL, it must lower it before calling IoCallDriver.
Constraints on Processing IRPs
If the IRP parameters include any user-space addresses, these must be validated before they are used. For more information, see Errors in Buffered I/O.
Additionally, if the IRP contains an IOCTL or FSCTL buffer that was sent from a 32-bit platform to a 64-bit platform, the buffer contents may need to be thunked. For more information, see Supporting 32-Bit I/O in Your 64-Bit Driver.
Unlike file systems, file system filter drivers should never call FsRtlEnterFileSystem or FsRtlExitFileSystem except before calling ExAcquireFastMutexUnsafe or ExAcquireResourceExclusiveLite. FsRtlEnterFileSystem and FsRtlExitFileSystem disable normal kernel APCs, which are needed by most file systems.
Constraints on Completing IRPs
When completing IRPs, file system filter drivers should use only success and error status values.
Although STATUS_PENDING is a success NTSTATUS value, it is a programming error to complete an IRP with STATUS_PENDING.
After a dispatch routine calls IoCompleteRequest, the IRP pointer is no longer valid and cannot safely be dereferenced.
Constraints on Setting a Completion Routine
Note: For information about setting completion routines, see Using Completion Routines.
When a dispatch routine calls IoSetCompletionRoutine, it can optionally pass a Context pointer to a structure for the completion routine to use when processing the given IRP. This structure must be allocated from nonpaged pool, because the completion routine can be called IRQL DISPATCH_LEVEL.
If a dispatch routine sets a completion routine that may return STATUS_MORE_PROCESSING_REQUIRED, it must do one of the following to prevent the I/O Manager from completing the IRP prematurely:
Constraints on Passing IRPs Down
After a dispatch routine calls IoCallDriver, the IRP pointer is no longer valid and cannot safely be dereferenced, unless the dispatch routine waits for the completion routine to signal that it has been called.
It is a programming error to call PoCallDriver from a file system filter driver. (PoCallDriver is used to pass IRP_MJ_POWER requests to lower-level drivers. File system filter drivers never receive IRP_MJ_POWER requests.)
Constraints on Returning Status
Except when completing an IRP, a dispatch routine that does not set a completion routine should always return the NTSTATUS value returned by IoCallDriver. Unless this value is STATUS_PENDING, it must match the value of Irp->IoStatus.Status set by the driver that completed the IRP.
When IoCallDriver returns STATUS_PENDING, the dispatch routine should also return STATUS_PENDING, unless it waits for the completion routine to signal an event.
When posting the IRP to a worker queue for later processing, the dispatch routine should mark the IRP pending and return STATUS_PENDING.
When setting a completion routine that might post the IRP to a worker queue for later processing, the dispatch routine should mark the IRP pending and return STATUS_PENDING.
A dispatch routine that marks an IRP pending must return STATUS_PENDING.
Oplock operations cannot be pended (posted to a worker queue), and dispatch routines cannot return STATUS_PENDING for them.
Constraints on Posting IRPs to a Work Queue
- If a dispatch routine posts IRPs to a work queue, it must call IoMarkIrpPending before posting each IRP to the worker queue. Otherwise, the IRP could be dequeued, completed by another driver routine, and freed by the system before the call to IoMarkIrpPending occurs, thereby causing a crash.