Using Neither Buffered Nor Direct I/O
If a driver is using neither buffered nor direct I/O, then the I/O manager passes the original user-space virtual addresses in IRPs that it sends to the driver. To access these buffers safely, the driver must be executing in the context of the calling thread. Typically, therefore, only highest-level drivers, such as FSDs, can use this method for accessing buffers.
An intermediate or lowest-level driver cannot always meet this condition. For example, if a requesting thread waits on the completion of an I/O request or if a higher-level driver is layered over the intermediate or lowest-level driver, then the lower-level driver's routines are unlikely to be called in the context of the requesting thread.
The I/O manager determines that an I/O operation is using neither buffered nor direct I/O as follows:
For IRP_MJ_READ and IRP_MJ_WRITE requests, neither DO_BUFFERED_IO nor DO_DIRECT_IO are set in the Flags member of the DEVICE_OBJECT structure. For more information, see Initializing a Device Object.
For IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL requests, the IOCTL code's value contains METHOD_NEITHER as the TransferType value in the IOCTL value. For more information, see Defining I/O Control Codes.
When a driver receives an IRP that specifies an I/O operation using neither buffered nor direct I/O, it must do the following:
Check the validity of the user buffer's address range and check whether the appropriate read or write access is permitted, using the ProbeForRead and ProbeForWrite support routines. The driver must enclose its accesses to the buffer's address range within a driver-supplied exception handler, so that a user thread cannot change the access rights for the buffer while the driver is accessing memory. If the probe raises an exception, the driver should return an error. The driver must call these routines within the context of the thread that made the I/O request; therefore, only a higher-level driver can perform this task.
Manage buffers and memory operations in one of the following ways:
- Carry out its own double-buffering operations, as the I/O manager does for drivers that use buffered I/O. For more information, see Using Buffered I/O.
- Create its own MDLs and lock down the buffer by calling the memory manager's support routines, as the I/O manager does for drivers that use direct I/O. For more information, see Using Direct I/O.
- Perform all necessary operations on the user buffer directly in the context of the calling thread. The driver must wrap its access to the buffer within a driver-supplied exception handler, in case a user thread changes either the access rights for the buffer or the data in the buffer while the driver is accessing memory. For more information, see Handling Exceptions.
In effect, the driver must choose on a per-IRP basis whether to do buffered I/O, direct I/O, or I/O in the context of the calling thread, and it must handle any exceptions that might occur in a user-mode thread context. The driver must manage its own user buffer accesses, double-buffering operations, and memory mappings, as necessary, instead of letting the I/O manager handle these operations for the driver.