Accessing buffers in PostOperationCallback

I am writing a minifilter to catch NotifyDirectory IRPs as they travel down
and up the filter stack. I have observed some very odd behavior in how the
buffer is handled. Consider the following code:

MyPreOperationCallback( … )
{
if (Data->Iopb->MinorFunction != IRP_MN_NOTIFY_CHANGE_DIRECTORY)
return FLT_PREOP_SUCCESS_NO_CALLBACK;
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

MyPostOperationCallback( … )
{
PVOID buffer;

FltLockUserBuffer(Data);
buffer = MmGetSystemAddressForMdlSafe(
Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress,
NormalPagePriority);

return FLT_POSTOP_FINISHED_PROCESSING;
}

If I run this filter and examine the contents of buffer in my PostOp
callback, it always contains the file that changed on the previous trip
through the filter chain. This is kind of hard to explain so let me provide
an example:

  1. With the filter loaded and attached (altitude 430000 for testing
    purposes), I start a user-mode app that monitors a directory for changes.
  2. My PreOp callback gets called as the user-mode app sends down an IRP for
    NotifyDirectory.
  3. I create subdirectory ‘1234’
  4. My PostOp callback gets called, buffer contains junk.
  5. My PreOp callback gets called as the user-mode app sends down another
    IRP.
  6. I create subdirectory ‘5678’
  7. My PostOp callback gets called, buffer contains ‘1234’ !!

I believe that I’m seeing a delayed version of the buffer because the
user-mode app is reusing the buffer from the previous trip and my filter is
somehow always seeing the pre-modified buffer. My first guess for why this
was happening was that a lower-level filter was swapping buffers and I was
seeing only the original buffer, so I rewrote the code as such:

MyPostOperationCallback( … )
{
PVOID buffer;
PMDL mdl;
mdl = FltGetSwappedBufferMdlAddress(Data);
if (mdl)
buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
}

This modification resulted in buffer never being set because mdl was always
NULL.

Next, I modified the code so that the user buffer was locked during the
PreOp:

MyPreOperationCallback( … )
{
if (Data->Iopb->MinorFunction != IRP_MN_NOTIFY_CHANGE_DIRECTORY)
return FLT_PREOP_SUCCESS_NO_CALLBACK;

FltLockUserBuffer(Data);

return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

MyPostOperationCallback( … )
{
PVOID buffer;

buffer = MmGetSystemAddressForMdlSafe(
Data->Iopb->Parameters.DirectoryControl.NotifyDirectory.MdlAddress,
NormalPagePriority);

return FLT_POSTOP_FINISHED_PROCESSING;
}

This solution actually worked!! I was able to see the filename without the
one trip delay through the filter chain. Apparently, I need to lock the
buffer during the PreOp so I can see it during the PostOp…?

I am trying to understand why I am observing this behavior before I move on
to the next phase of my project. I’ve read through all of the WDK
documentation, the Filter Driver Developer Guide, and searched through the
osr archives and so far I can’t figure out what’s going on here. Can anybody
shed some light on this?

Best Regards,
Michael Carr

I am a bit surprised, since generally file system drivers don’t use direct I/O for their operations. Thus, I’m a tad bit shocked there’s even an MDL. Normally, data is returned directly in the user buffer. But then again, the change notification package is in the FsRtl (and posted) so I could see them locking down the buffer when they need to post something.

Still, it makes me wonder, what happens if the change is reported immediately (no posting, no need for an MDL.)

Bottom line, I’m not even sure why you have an MDL in the first place. I suspect that locking it down in the pre-process path just ensures that you always know what the MDL is.

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com