I’m in the process of writing my first minifilter driver and I’m confused about how to properly access files from my driver. First let me apologize if the answer to these questions are obvious. Whereas I’m an experienced Windows developer, this is my first foray into device drivers.
What I’m trying to do is fairly straight forward. I want to monitor all hard drive volumes for activity, and write information about that activity to an output file. As a starting point I used the “minispy” sample in the DDK. My minifilter project is monitoring all the disk activity I’m concerned with, I’m at the point where I need to write the output to a file. This file will be on the local system so it will be on a volume which minifilter driver is monitoring. As I understand it I don’t want to call ZwCreateFile, this would be reentrant and would cause problems. I need to use one of the three FltCreateFile APIs and pass the Instance for the volume on which I’m creating the file. This is where I’m running into problems.
I don’t have any of the FLT_INSTANCE objects for my filter. I do not explicitly call FltAttachVolume, my filter automatically attaches to all volumes at startup. To get the Instance object I believe I can call FltGetVolumeName, then call FltGetVolumeInstanceFromName, then finally call FltCreateFile.
Am I on the right track here, or am I missing something obvious? Thanks.
So if you’re basing this on the minispy sample, then the instance of your
filter for the operation you’re currently processing is in the FltObjects
parameter to the SpyPreOperationCallback and SpyPostOperationCallback.
I’ve gotten past my initial confusion, but now Driver Verifier is detecting a fault in my driver but I can’t find what I’m doing wrong. I’m getting DRIVER_VERIFIER_IOMANAGER_VIOLATION - Irql not equal across call to the driver dispatch routine. The description for this error is straight forward, yet I’ve looked at my code and I don’t see how this is happening. I don’t explicitly change the IRQL, nor am I using spin locks. Here are some details about the error:
I have two possible guesses as to what the source of the problem is, but I’m just guessing.
I’m effectively calling FltWriteFile from my Post event callback. But as I understand it, this shouldn’t be a problem, the reason FltWriteFile takes an Instance object is to prevent reentrant problems.
I’m calling FltCreateFile from a different function than I’m calling FltWriteFile. The file is opened once when the driver is initialized, then the file is written to during Post event calls, and finally closed when the driver is shutdown. As far as I know it’s safe to write to a file opened from a different function.
Am I missing something obvious here? Any suggestions how to track down this problem? Thanks.
Well, there are some additional restrictions that govern this scenario.
First, the postOperation callback is not guaranteed to be at PASSIVE (even
if the preOp is), and so calling FltWriteFile from any postOp is a problem.
Then issuing synchronous IO in a postOp callback might be a problem,
irrespective of the IRQL… See the documentation page for
FltDoCompletionProcessingWhenSafe (which might be a good function to use
btw).
If the number of IRPs you want to process is small and they are rather
infrequent you might want to consider synchronizing the operations (by
returning FLT_PREOP_SYNCHRONIZE from the preOp callback for the operations
you are interested in logging). This can have a negative impact on system
performance overall (because the OS itself does make use heavy of
asynchronous operations in some cases), but you are guaranteed that you will
be in the same thread context as the preOp and at the same IRQL.
If you plan to call FltWriteFile in the context of IO operation (like
IRP_MJ_READ, IRP_MJ_WRITE) then there are additional things you need to be
aware of, depending on the type of IO…
What are you trying to do with the logs? What is the purpose of your product
(at a high level) ?
Here’s a high-level summary of what I’m trying to do:
I need to monitor the system for every modification to the file system (and eventually the registry). So my minifilter driver monitors for things like IRP_MJ_CREATE, IRP_MJ_WRITE, IRP_MJ_SET_INFORMATION, etc. For each of these events it records the type of event, input parameters, output results, and the path to the object. All this info is written to a log file where, later on, a user-mode app will process that information.
I’m buffering these events in my driver in a non-paged look aside list, and occasionally flush the buffers to the file.
I ran into the problem if Post event callback at an IRQL > PASSIVE, so in my code I was only attempting to flush the buffers if the IRQL == PASSIVE.
I’ll have a look at FltDoCompletionProcessingWhenSafe. I’ll also look at FLT_PREOP_SYNCHRONIZE, although since I am concerned with IRP_MJ_WRITE events this may not be the best idea. I may even try moving the flush call into the Pre event callback instead of the Post.
You say calling FltWriteFile in the context of certain IO operations has additional things to be aware of. Where can I find documentation on this?
So what I would do would be to create each log entry in the preOp and
capture the parameters and then pass it to the postOp in the context of the
operation. In the postOp I would get the result and add it to the in-memory
log (assuming you don’t want to discard it based on the operation result).
In terms of flushing, I would use a separate thread, completely asynchronous
from any operation on the system (depending on a timer and the size of the
log and the number of queued events perhaps…).
Unfortunately I’m not aware of any centralized place where all the
restrictions are documented… Most of them can be inferred from the rules
about writing filesystems in Rajeev’s book. Searching the archives of this
list is also a pretty good source of information, though it helps to know
what you’re looking for (for example, if you want to issue IO in the context
of a paging write then that IO must be paging IO as well, see http://www.osronline.com/ShowThread.cfm?link=156969). Anyway, my suggestion
would be to do all log IO from a different thread to keep things simple.
Alex, thanks so much for the help. I did as you suggested and created a separate thread to handle flushing the buffers. Not only does this work great (no more crashes), but my code is now simpler to.