- Start with a reliable regression test suite to verify behavior between the legacy filter driver and the ported minifilter driver.
- Create a minifilter driver shell and systematically move functionality from the legacy filter driver to the minifilter driver. For example, get attachment working and then port one operation at a time, testing after each operation.
- Change user-mode/kernel-mode communication last, so you can use existing tools to test the minifilter driver.
- Compile with PREfast and test with the Filter Verifier I/O verification option in Driver Verifier enabled.
- IRP-based I/O and fast I/O operations can come through the same operation when appropriate, which helps reduce duplication of code.
- When registering for operations, a minifilter driver can explicitly choose to ignore all paging I/O and cached I/O, which eliminates the need for code to check these.
- Instance notifications greatly simplify attach/detach logic.
- Register only for operations that your minifilter driver must handle; you can ignore everything else.
- Take advantage of filter manager context and name management support.
- Take advantage of filter manager support for issuing non-recursive I/O.
- Unlike legacy filter drivers, minifilter drivers cannot rely on local variables to maintain context from preoperation processing to postoperation processing. Consider allocating a lookaside list to store operation state.
- Be sure to release references when finished with a name or context.
- Completion ports in user mode add a powerful technique for building queues. You will probably need only a single connection to a single named port.
Legacy filter driver model | Filter manager model |
---|---|
Pass-through operation with no completion routine | If your minifilter driver never does work for this type of I/O operation, do not register a preoperation or postoperation callback routine for this operation. Otherwise, return FLT_PREOP_SUCCESS_NO_CALLBACK from the preoperation callback routine registered for this operation. See Returning FLT_PREOP_SUCCESS_NO_CALLBACK. |
Pass-through operation with a completion routine | Return FLT_PREOP_SUCCESS_WITH_CALLBACK from the preoperation callback routine. See Returning FLT_PREOP_SUCCESS_WITH_CALLBACK. |
Pend operation in the preoperation callback routine | Call FltLockUserBuffer as needed to ensure that any user buffers are properly locked so that they can be accessed in a worker thread. Queue the work to a worker thread by calling support routines such as FltAllocateDeferredIoWorkItem and FltQueueDeferredIoWorkItem. Return FLT_PREOP_PENDING from the preoperation callback routine. When ready to return the I/O operation to the filter manager, call FltCompletePendedPreOperation. See Pending an I/O Operation in a Preoperation Callback Routine. |
Pend operation in the postoperation callback routine | In the preoperation callback routine, call FltLockUserBuffer to ensure that user buffers are properly locked so that they can be accessed in a worker thread. Queue the work to a worker thread by calling support routines such as FltAllocateGenericWorkItem and FltQueueGenericWorkItem. Return FLT_POSTOP_MORE_PROCESSING_REQUIRED from the postoperation callback routine. When ready to return the I/O operation to the filter manager, call FltCompletePendedPostOperation. See Pending an I/O Operation in a Postoperation Callback Routine. |
Synchronize the operation | Return FLT_PREOP_SYNCHRONIZE from the preoperation callback routine. See Returning FLT_PREOP_SYNCHRONIZE. |
Complete the operation in the preoperation callback routine | Set the final operation status and information in the IoStatus member of the FLT_CALLBACK_DATA structure for the operation. Return FLT_PREOP_COMPLETE from the preoperation callback routine. See Completing an I/O Operation in a Preoperation Callback Routine. |
Complete the operation after it has been pended in the preoperation callback routine | Set the final operation status and information in the IoStatus member of the FLT_CALLBACK_DATA structure for the operation. Call FltCompletePendedPreOperation from the worker thread processing the I/O operation, passing FLT_PREOP_COMPLETE as the CallbackStatus parameter. See Completing an I/O Operation in a Preoperation Callback Routine. |
Do all completion work in the completion routine | Return FLT_POSTOP_FINISHED_PROCESSING from the postoperation callback routine. See Writing Postoperation Callback Routines. |
Do completion work at safe IRQL | Call FltDoCompletionProcessingWhenSafe from the postoperation callback routine. See Ensuring that Completion Processing is Performed at Safe IRQL. |
Signal an event from the completion routine | Return FLT_PREOP_SYNCHRONIZE from the preoperation callback routine for this operation. The filter manager calls the postoperation callback routine in the same thread context as the preoperation callback routine, at IRQL <= APC_LEVEL. See Returning FLT_PREOP_SYNCHRONIZE. |
Fail a successful create operation | Call FltCancelFileOpen from the postoperation callback routine for the create operation. Set an appropriate error NTSTATUS value in the IoStatus member of the FLT_CALLBACK_DATA structure for the operation. Return FLT_POSTOP_FINISHED_PROCESSING. See Failing an I/O Operation in a Postoperation Callback Routine. |
Disallow I/O through the fast I/O path for an I/O operation | Return FLT_STATUS_DISALLOW_FAST_IO from the preoperation callback routine for the operation. See Disallowing a Fast I/O Operation in a Preoperation Callback Routine. |
Modify the parameters for an I/O operation | Set the modified parameter values in the Iopb member of the FLT_CALLBACK_DATA structure for the operation. Mark the FLT_CALLBACK_DATA structure as dirty by calling FltSetCallbackDataDirty, except when you have modified the contents of the IoStatus member of the FLT_CALLBACK_DATA structure. See Modifying the Parameters for an I/O Operation. |
Lock the user buffer for the operation | Use the techniques and guidelines described in Accessing the User Buffers for an I/O Operation. |
No comments:
Post a Comment