3.2.4.3 Using Neither Direct Nor Buffered I/O

Setting up device objects for neither direct nor buffered I/O causes the I/O Manager to pass the original user-space virtual addresses in IRPs sent to the driver. Because such a driver must be executing in the context of the calling thread to access its buffer(s) safely, only highest-level NT drivers, such as FSDs, can set up their device objects without setting a bit in each device object’s Flags by ORing it with DO_DIRECT_IO or DO_BUFFERED_IO.

Note that an NT 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 (particularly a file system) is layered over the intermediate or lowest-level driver, such a lower-level driver’s routines are unlikely to be called in the context of the requesting thread.

When the I/O Manager sends an IRP with the current thread’s user-space virtual addresses for a buffer, a driver that did not OR its device object(s)’ Flags with DO_BUFFERED_IO or DO_DIRECT_IO must do the following:

  1. Check the validity of the user buffer’s address range and check whether the appropriate read or write access is permitted to the buffer. Such a driver must wrap its accesses to the buffer’s address range within a driver-supplied exception handler in case a user thread attempts to change the access rights for the buffer while the driver is accessing memory.

  2. Do one of the following:

    • Carry out its own double-buffering operations, as the I/O Manager does for drivers that use 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.

    • Perform all necessary operations on the user buffer directly in the context of the calling thread. Such a 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.

In effect, such a 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. Such a 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.