Mini-filter PreWrite callback code not invoked when p4v.exe writes files.

Hello,

I am writing a mini-filter driver that performs encryption on NTFS
volumes on Windows 7. The altitude of the filter is 141000. The
driver registers callbacks for IRP_MJ_WRITE (plus sevel others) as
follows:

CONST FLT_OPERATION_REGISTRATION Callbacks = {

{ IRP_MJ_WRITE,
0, // no flags
NLFSEOpCallbackPreWrite,
NLFSEOpCallbackPostWrite },

};

CONST FLT_REGISTRATION FilterRegistration = {

sizeof( FLT_REGISTRATION ), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
ContextNotifications, // Context
Callbacks, // Operation callbacks
FilterUnload, // MiniFilterUnload
InstanceSetup, // InstanceSetup
NULL, // InstanceQueryTeardown
InstanceTeardownStart, // InstanceTeardownStart
NULL, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL // NormalizeNameComponent
};

// Register with FltMgr
status = FltRegisterFilter( DriverObject,
&FilterRegistration,
&nlfseGlobal.filterHandle );

NLFSEOpCallbackPreWrite() encrypts the data before it is written to
disk. It does so in the following manner:

try{

if (!(iopb->IrpFlags &
(IRP_NOCACHE | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO)))
{
//we only care about these IO operation
leave;
}

… encrypt the data here …
}
finally
{
}

The driver mostly works. When testing with user “apps” like IE,
Notepad, cmd.exe, The data written by the user app reaches the
encryption code in NLFSEOpCallbackPreWrite(), and the disk content
ends up being is encrypted.

However, when we test it with p4v.exe (Perforce Visual
Client/NTX86/2008.1/164135) as the user application (i.e. encrypting
the source code that p4v.exe retrieves to local disk), sometimes
encryption doesn’t occur. When the user tries to retrieve a certain
revision of a file from Perforce server, the file data never reaches
the encryption code in NLFSEOpCallbackPreWrite(). As a result, the
file on disk is not encrypted.

The problem only happens to some files in a directory, but not other
files in the same directory. It also only happens to some Perforce
revisions of a file, but not to other revisions of the same file.
(E.g. the problem happens to Makefile#6 (518 bytes), but not
Makefile#7 (527 bytes).) It is 100% reproducible for the same files
and revisions that experience the problem. It happens even when the
user retrieves the file one at a time.

I used Process Monitor v2.91 to watch the IO on the local file. For
files/revisions that don’t have the problem, Process Monitor shows
FASTIO_ACQUIRE_FOR_CC_FLUSH and FASTIO_RELEASE_FOR_CC_FLUSH for the
file. For files/revisions that have the problem, Process Monitor
doesn’t display such operations. However, I’m not sure whether this
is the cause of the problem, or the effect of some bad code in my
filter driver.

Any suggestion on how I can debug this? Thanks in advance for any
info!

— Allen Yuen

This email has been inspected by NextLabs’ Outlook Policy Enforcerhttp: to ensure compliance

- --------------------------------------------------------------------
STATEMENT OF CONFIDENTIALITY

The information contained in this electronic message and any attachments to this message are intended for the exclusive use of the addressee(s) and may contain confidential or privileged information. No representation is made on its accuracy or completeness of the information contained in this electronic message. Certain assumptions may have been made in the preparation of this material as at this date, and are subject to change without notice. If you are not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this e-mail and any attachment(s) is strictly prohibited. Please reply to the sender at NextLabs Inc and destroy all copies of this message and any attachments from your system. ======================================================================</http:>

Sorry, I overlooked some code. Here is more info. What really
happens is that for those Perforce file revisions where the problem
happens, my PreWrite callback is indeed called. However, for some
reason FO_STREAM_FILE is set in FltObjects->FileObject->Flags. Hence
my code thinks that the file is not a plain file that we care, and
skips encrypting the file content.

My code with more details actually looks like this:

if (FltObjects->FileObject->Flags & FO_STREAM_FILE) {
return FLT_PREOP_SUCCESS_NO_CALLBACK; // ignore this call
}

try{

if (!(iopb->IrpFlags &
(IRP_NOCACHE | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO)))
{
//we only care about these IO operation
leave;
}

… encrypt the data here …
}
finally
{
}

If I add some hack to force the code to not ignore this call, the file
is encrypted on disk correctly.

Now my question becomes: why is FO_STREAM_FILE set in the FileObject
of a plain file passed to a a PreWrite callback? And why does this
happen to only certain plain files?

The difference between the good case and the bad case are that:

Good:
Data->Iopb->IrpFlags=0x00060043
FO_STREAM_FILE in FltObjects->FileObject->Flags is set.

Bad:
Data->Iopb->IrpFlags=0x00060043
FO_STREAM_FILE in FltObjects->FileObject->Flags is clear.

Any info is appreciated. Thanks.

— Allen Yuen

Sorry! Correction:

Good:
Data->Iopb->IrpFlags=0x00060043
FO_STREAM_FILE in FltObjects->FileObject->Flags is *clear*.

Bad:
Data->Iopb->IrpFlags=0x00060043
FO_STREAM_FILE in FltObjects->FileObject->Flags is *set*.

— Allen Yuen

On 7/26/2010 1:00 PM, xxxxx@nextlabs.com wrote:

Sorry, I overlooked some code. Here is more info. What really
happens is that for those Perforce file revisions where the problem
happens, my PreWrite callback is indeed called. However, for some
reason FO_STREAM_FILE is set in FltObjects->FileObject->Flags. Hence
my code thinks that the file is not a plain file that we care, and
skips encrypting the file content.

Not sure why you think it is a hack or that you should ignore them in IO
processing but …

Stream file objects are used by NTFS for initializing caching on a file.
Hence paging requests can be issued using that file object. Therefore
you do need to handle the cases when this flag is set, it is not a hack
at all.

Note that the SFO flag simply indicates, in normal cases, that an open
was never issued on that given file object but it is definitely
associated to an open file or stream of data. Hence if you get it back
from a stream handle lookup then don’t ignore it.

Pete

My code with more details actually looks like this:

if (FltObjects->FileObject->Flags& FO_STREAM_FILE) {
return FLT_PREOP_SUCCESS_NO_CALLBACK; // ignore this call
}

try{

if (!(iopb->IrpFlags&
(IRP_NOCACHE | IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO)))
{
//we only care about these IO operation
leave;
}

… encrypt the data here …
}
finally
{
}

If I add some hack to force the code to not ignore this call, the file
is encrypted on disk correctly.

Now my question becomes: why is FO_STREAM_FILE set in the FileObject
of a plain file passed to a a PreWrite callback? And why does this
happen to only certain plain files?

The difference between the good case and the bad case are that:

Good:
Data->Iopb->IrpFlags=0x00060043
FO_STREAM_FILE in FltObjects->FileObject->Flags is set.

Bad:
Data->Iopb->IrpFlags=0x00060043
FO_STREAM_FILE in FltObjects->FileObject->Flags is clear.

Any info is appreciated. Thanks.

— Allen Yuen


NTFSD is sponsored by OSR

For our schedule of debugging and file system seminars
(including our new fs mini-filter seminar) visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer


Kernel Drivers
Windows File System and Device Driver Consulting
www.KernelDrivers.com
866.263.9295

Thank you Peter! I thought the FO_STERAM_FILE flag was strictly associated with file streams like “myFile.txt:myStream1”. Since our product does not handle file streams, I though the right thing to do was to check for FO_STREAM_FILE flag in the PreWrite, and ignore the operation if it is set.

After I fixed my code to not rely on the FO_STREAM_FILE bit per your reply, it now works correctly!

Thank you very much!

— Allen Yuen