Minifilter confusion

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.

Hello !

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.

Thanks,
Alex.

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:


Previous IRQL: 0
Current IRQL: 0
Callstack:
b876e328 80ac858d 000000c9 00000005 8981d020 nt!KeBugCheckEx+0x1b
b876e364 80629f46 f7b7574d 898d2000 8904c550 nt!IovCallDriver+0x193
b876e37c f7b7574d 8904c550 8981eee8 80bc1dc4 nt!IofCallDriver+0x1c
b876e3ac 80ac854a 8981eee8 8904c6bc 8981eee8 fltmgr!FltpDispatch+0x29d
b876e3dc 80629f46 f7b75198 8075a628 00000000 nt!IovCallDriver+0x150
b876e3f4 f7b75198 89389f5c 8075a628 00000000 nt!IofCallDriver+0x1c
b876e41c f7b78716 b876e43c 893ad5a0 00000000 fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x3cc
b876e45c f7b793d6 00000000 896ae8bc 8905c8a0 fltmgr!FltPerformSynchronousIo+0x1c8
b876e488 f77304d5 896ad5e8 006b0d40 00000000 fltmgr!FltWriteFile+0x1d2
b876e4cc f773034b 896ae8bc 00000000 00000001 discovery!FlushEvents+0x115 [c:\projects\discovery\filter\discovery.c @ 490]
b876e4f4 f7bce652 896ae8bc b876e540 88d78df8 discovery!PostOperationCallback+0xab [c:\projects\discovery\filter\discovery.c @ 410]
b876e51c f7b63b5a 00000022 0176e540 88d78df8 fltmgr!FltvPostOperation+0x7c
b876e58c f7b73020 006ae860 896ae860 b876e5b4 fltmgr!FltpPerformPostCallbacks+0x4b8
b876e59c f7b74765 896ae860 88d79630 b876e63c fltmgr!FltpProcessIoCompletion+0x52
b876e5b4 80ac823e 893ad5a0 88d79630 896ae860 fltmgr!FltpPassThroughCompletion+0x1b7
b876e5d8 8062a350 893ad5a0 88d79630 b876e63c nt!IovpLocalCompletionRoutine+0xb4
b876e608 80ac87bc e25ade38 e25ade38 88d79630 nt!IopfCompleteRequest+0x124
b876e674 80932686 e25ade38 e25ade58 00000000 nt!IovCompleteRequest+0x9a
b876e6a4 809326d0 88d79630 e25ade38 00000000 nt!FsRtlNotifyCompleteIrp+0x158
b876e6cc 8093346d e25ade38 00000000 e25f9cd0 nt!FsRtlNotifyCompleteIrpList+0x3c
b876e754 ba75a6f0 89947200 8981d420 e25f9ef0 nt!FsRtlNotifyFilterReportChange+0x6d7
b876e990 ba75cab3 b876e9ac 8a31ae48 8a31ae48 Ntfs!NtfsCommonCleanup+0x13c4
b876eb00 80ac854a 8981d020 8a31ae48 8981d020 Ntfs!NtfsFsdCleanup+0x103
b876eb30 80629f46 f7b75701 00000000 8a31ae48 nt!IovCallDriver+0x150
b876eb48 f7b75701 8a31ae48 8981eee8 80bc1dc4 nt!IofCallDriver+0x1c
b876eb78 80ac854a 8981eee8 8a31afb4 8981eee8 fltmgr!FltpDispatch+0x251
b876eba8 80629f46 f7b75198 8075a628 00000000 nt!IovCallDriver+0x150
b876ebc0 f7b75198 00000000 8a31ae48 893ad5a0 nt!IofCallDriver+0x1c
b876ebe8 f7b7562a b876ec08 893ad5a0 00000000 fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x3cc
b876ec24 80ac854a 893ad5a0 8a31ae48 893ad5a0 fltmgr!FltpDispatch+0x17a
b876ec54 80629f46 8095501c 896ae9c8 8a31ae48 nt!IovCallDriver+0x150
b876ec6c 8095501c 899255cc 80bc1dc4 89925538 nt!IofCallDriver+0x1c
b876ec9c 809dcea1 89422880 893ad5a0 00010080 nt!IopCloseFile+0x476
b876ecd0 809dbf21 89422880 896ae9c8 00925538 nt!ObpDecrementHandleCount+0x1fb
b876ecfc 809dc0bc e244e898 00000000 00000038 nt!ObpCloseHandleTableEntry+0x1c9
b876ed48 809dc245 00000038 00000001 b876ed64 nt!ObpCloseHandle+0x118
b876ed58 80777a45 00000038 01eff69c 7c9493a4 nt!NtClose+0x1b
b876ed58 7c9493a4 00000038 01eff69c 7c9493a4 nt!KiFastCallEntry+0x135
WARNING: Frame IP not in any known module. Following frames may be wrong.
01eff69c 00000000 00000000 00000000 00000000 0x7c9493a4

I have two possible guesses as to what the source of the problem is, but I’m just guessing.

  1. 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.

  2. 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) ?

Thanks,
Alex.

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?

Thanks.
Brian

Hi Brian,

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.

Thanks,
Alex.

> 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.

So, the producer is >= DISPATCH, and the consumer is PASSIVE. Why it this bad?


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

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.

Brian

Glad I could help!

Thanks,
Alex.