Precreate versus Postcreate

I have a AV scanner in user space. I have my filter driver attached to the drive I wish to scan. When a file is dragged/written into the drive, I wish to scan it. I just wish to get the filename from the filter driver and pass it on to user space. If the scan detects a virus, I want to cancel the create.

The scanner sample driver does a file read(and scanning) in postcreate.

For what I wish to do, can I just get the file name in precreate and call FltSendMessage() in precreate itself?

If you understand the answers available in the PreCreate callback. This of
course implies you know the questions you need to ask. I would suggest
using filetest and its various options to see how each type of request is
handled and the information available at each stage of processing. If you
don’t understand EVERY word of the MSDN page for CreateFile, you should not
attempt this and don’t forget there are options available via NtCreateFile
that are not present in the CreateFile API.

Build a spreadsheet and indicate in each cell each bit of information
present in a NtCreateFile call in the two callbacks. See what you need to
know for your driver and use colors to mark each cell accordingly.

wrote in message news:xxxxx@ntfsd…
>I have a AV scanner in user space. I have my filter driver attached to the
>drive I wish to scan. When a file is dragged/written into the drive, I wish
>to scan it. I just wish to get the filename from the filter driver and pass
>it on to user space. If the scan detects a virus, I want to cancel the
>create.
>
> The scanner sample driver does a file read(and scanning) in postcreate.
>
> For what I wish to do, can I just get the file name in precreate and call
> FltSendMessage() in precreate itself?
>
>

Well, generally scanning in preCreate is not a good strategy. In fact, it’s generally not a good idea for minifilters to block preCreates and wait for a user mode process to open and process the file targeted by the create (most minifilters doing this would be AV filters i guess).

InstanceSetup is called by filter manager when it sees the first operation for a volume. In this case filter manager will block all operations on the volume and call the InstanceSetup callback for all the attached minifilters, and only after they all return from their respective InstanceSetup will filter manager allow operations on the volume to proceed (because otherwise the minifilters that didn’t complete InstanceSetup might miss some operations). If a minifilter above yours wants to open a file in its InstanceSetup (which is legal) you might end up blocking it in your minifilter and waiting for it to be scanned. Your user mode scanner will never make progress because it will block behind filter manager waiting for InstanceSetup to complete and you have a deadlock.

Incidentally, minifilters should be very careful about what they do in InstanceSetup, waiting there might lead to deadlocks…

There are other reasons why this isn’t great, but i hope the example above is serious enough to convince you to stick with postCreate…

Thanks,
Alex.

Thanks David and Alex.

Alex: I understand why you think scanning in pre-create is not a good idea. I will stick to post-create.
I pass the file name from pre-create to post-create using completionContext. But I have a doubt - please pardon me if it’s a very basic question. When I copy a file to the drive, I see multiple calls to postcreate(with the same file name) and hence multiple calls to the user land scanner. Is this to be expected? If not,what could be causing this?

Hi.

Why do you pass the filename from pre to post? The scanner sample doesn’t do
that either … for a good reason: Calling FltGetFileNameInformation in
PostCreate will make FilterManager’s life much easier.

Seeing multiple IRP_MJ_CREATEs for e.g. a copy operation is normal. Examine
the flags of the operation and maintain a “Context” for every stream in a
file to avoid multiple scans in a row. The “ctx” sample demonstrates how to
handle “Contexts”.

Good luck

wrote news:xxxxx@ntfsd…
> Thanks David and Alex.
>
> Alex: I understand why you think scanning in pre-create is not a good
> idea. I will stick to post-create.
> I pass the file name from pre-create to post-create using
> completionContext. But I have a doubt - please pardon me if it’s a very
> basic question. When I copy a file to the drive, I see multiple calls to
> postcreate(with the same file name) and hence multiple calls to the user
> land scanner. Is this to be expected? If not,what could be causing this?
>
>

Thanks Frank. I noticed that the current scanner code already uses a Rescan flag in the scannerContext. So I tried something similar by adding another flag, but I still get multiple re-scans.

PostCreate(…)
{

// If this create was failing anyway, don’t bother scanning now.
if (!NT_SUCCESS( Data->IoStatus.Status ) ||
(STATUS_REPARSE == Data->IoStatus.Status)) {
return FLT_POSTOP_FINISHED_PROCESSING;
}

//Get File Name

// The create has requested write access, mark to rescan the file.
// Allocate the context.
status = FltAllocateContext( ScannerData.Filter,
FLT_STREAMHANDLE_CONTEXT,
sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
PagedPool,
&scannerContext );

if (scannerContext->AlreadyScanned == FALSE)
{
scanFile = ScannerpCheckExtension( &Name->Extension );
if (!scanFile) {
// Not an extension we are interested in
return FLT_POSTOP_FINISHED_PROCESSING;
}

DbgPrint( “!!! scanner.sys – Calling ScannerpScanFileInUserMode in postcreate !!!\n” );

status = ScannerpScanFileInUserMode( Data,
FltObjects->Instance,
FltObjects->FileObject,
&Name->FinalComponent,
&safeToOpen );
if (!safeToOpen) {
scannerContext->AlreadyScanned=TRUE;
status = FltSetStreamHandleContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
scannerContext,
NULL );

// Ask the filter manager to undo the create.
DbgPrint( “!!! scanner.sys – foul language detected in postcreate !!!\n” );
DbgPrint( “!!! scanner.sys – undoing create \n” );

FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject );

Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;

returnStatus = FLT_POSTOP_FINISHED_PROCESSING;

} else if (FltObjects->FileObject->WriteAccess) {
DbgPrint( “!!! scanner.sys – No foul language in the file \n” );

// The create has requested write access, mark to rescan the file.
// Allocate the context.
if (NT_SUCCESS(status))
{
scannerContext->AlreadyScanned=TRUE;
scannerContext->RescanRequired = TRUE;
status = FltSetStreamHandleContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
scannerContext,
NULL );

}//if
}
else
{
scannerContext->AlreadyScanned=TRUE;
status = FltSetStreamHandleContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
scannerContext,
NULL );
}
}//AlreadyScanned

FltReleaseFileNameInformation(Name);
FltReleaseContext( scannerContext );
return returnStatus;

}

hemasreeram, read my previous post again: I talked about Stream-Context
*not* StreamHandle-Context. Google for “FilterDriverDeveloperGuide.doc” and
read Chapter 9 to get the differences.

wrote news:xxxxx@ntfsd…
> Thanks Frank. I noticed that the current scanner code already uses a
> Rescan flag in the scannerContext. So I tried something similar by adding
> another flag, but I still get multiple re-scans.
>
> PostCreate(…)
> {
>
> // If this create was failing anyway, don’t bother scanning now.
> if (!NT_SUCCESS( Data->IoStatus.Status ) ||
> (STATUS_REPARSE == Data->IoStatus.Status)) {
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
>
> //Get File Name
>
> // The create has requested write access, mark to rescan the file.
> // Allocate the context.
> status = FltAllocateContext( ScannerData.Filter,
> FLT_STREAMHANDLE_CONTEXT,
> sizeof(SCANNER_STREAM_HANDLE_CONTEXT),
> PagedPool,
> &scannerContext );
>
> if (scannerContext->AlreadyScanned == FALSE)
> {
> scanFile = ScannerpCheckExtension( &Name->Extension );
> if (!scanFile) {
> // Not an extension we are interested in
> return FLT_POSTOP_FINISHED_PROCESSING;
> }
>
> DbgPrint( “!!! scanner.sys – Calling ScannerpScanFileInUserMode in
> postcreate !!!\n” );
>
> status = ScannerpScanFileInUserMode( Data,
> FltObjects->Instance,
> FltObjects->FileObject,
> &Name->FinalComponent,
> &safeToOpen );
> if (!safeToOpen) {
> scannerContext->AlreadyScanned=TRUE;
> status = FltSetStreamHandleContext( FltObjects->Instance,
> FltObjects->FileObject,
> FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
> scannerContext,
> NULL );
>
> // Ask the filter manager to undo the create.
> DbgPrint( “!!! scanner.sys – foul language detected in postcreate
> !!!\n” );
> DbgPrint( “!!! scanner.sys – undoing create \n” );
>
> FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject );
>
> Data->IoStatus.Status = STATUS_ACCESS_DENIED;
> Data->IoStatus.Information = 0;
>
> returnStatus = FLT_POSTOP_FINISHED_PROCESSING;
>
> } else if (FltObjects->FileObject->WriteAccess) {
> DbgPrint( “!!! scanner.sys – No foul language in the file \n” );
>
> // The create has requested write access, mark to rescan the file.
> // Allocate the context.
> if (NT_SUCCESS(status))
> {
> scannerContext->AlreadyScanned=TRUE;
> scannerContext->RescanRequired = TRUE;
> status = FltSetStreamHandleContext( FltObjects->Instance,
> FltObjects->FileObject,
> FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
> scannerContext,
> NULL );
>
>
> }//if
> }
> else
> {
> scannerContext->AlreadyScanned=TRUE;
> status = FltSetStreamHandleContext( FltObjects->Instance,
> FltObjects->FileObject,
> FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
> scannerContext,
> NULL );
> }
> }//AlreadyScanned
>
> FltReleaseFileNameInformation(Name);
> FltReleaseContext( scannerContext );
> return returnStatus;
>
> }
>
>

Thanks for the pointer, Frank. I looked into the ctx sample and now I get only one scan message.
However, I am having problem unloading the scanner(hang). The code below should release all ref counts for the context.

ScannerPostCreate (
__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__in_opt PVOID CompletionContext,
__in FLT_POST_OPERATION_FLAGS Flags
)
{
PSCANNER_STREAM_CONTEXT scannerContext;
FLT_POSTOP_CALLBACK_STATUS returnStatus = FLT_POSTOP_FINISHED_PROCESSING;
PFLT_FILE_NAME_INFORMATION Name;
PFLT_FILE_NAME_INFORMATION tunnelName;
PUNICODE_STRING scanName;
int i;
NTSTATUS status;
BOOLEAN safeToOpen=TRUE, Scanned =FALSE, scanFile;
int ref_count=0;

UNREFERENCED_PARAMETER( CompletionContext );
UNREFERENCED_PARAMETER( Flags );

// If this create was failing anyway, don’t bother scanning now.
if (!NT_SUCCESS( Data->IoStatus.Status ) ||
(STATUS_REPARSE == Data->IoStatus.Status)) {
return FLT_POSTOP_FINISHED_PROCESSING;
}

//Get File Name
status = FltGetStreamContext( FltObjects->Instance,
FltObjects->FileObject,
&scannerContext );
if (!(NT_SUCCESS(status)) && (status == STATUS_NOT_FOUND)) //No scannerContext exists
{
DbgPrint( “!!! scanner.sys --PostCreate: Error getting Stream context %d \n”, status );

//Create a stream context
status = FltAllocateContext( ScannerData.Filter,
FLT_STREAM_CONTEXT,
sizeof(SCANNER_STREAM_CONTEXT),
PagedPool,
&scannerContext );
if (! (NT_SUCCESS(status)))
{
DbgPrint( “!!! scanner.sys --PostCreate: Error allocating Stream context %d \n”, status );
FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);
return returnStatus;
}

DbgPrint( “!!! scanner.sys --PostCreate: Success allocating Stream context %d \n”, status );
RtlZeroMemory(scannerContext, sizeof(SCANNER_STREAM_CONTEXT));
scannerContext->AlreadyScanned = FALSE;
status = FltSetStreamContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
scannerContext,
NULL );
if (!NT_SUCCESS(status))
{
FltReleaseContext(scannerContext);
FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);

if (status != STATUS_FLT_CONTEXT_ALREADY_DEFINED)
return status;
}
}

if (scannerContext->AlreadyScanned == FALSE)
{
scanFile = ScannerpCheckExtension( &Name->Extension );
if (!scanFile) {
DbgPrint( “!!! scanner.sys --PostCreate: AlreadyScanned is set to false. Not interested \n” );
// Not an extension we are interested in

FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);

return FLT_POSTOP_FINISHED_PROCESSING;
}

DbgPrint( “!!! scanner.sys – Calling ScannerpScanFileInUserMode in postcreate !!!\n” );

status = ScannerpScanFileInUserMode( Data,
FltObjects->Instance,
FltObjects->FileObject,
&Name->FinalComponent,
&safeToOpen,
&Scanned);
if (!Scanned)
{
scannerContext->AlreadyScanned=FALSE;
status = FltSetStreamContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
scannerContext,
NULL );
if (!NT_SUCCESS(status))
{
FltReleaseContext(scannerContext);
FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);
}

// FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject );
DbgPrint( “!!! scanner.sys – Not scanned - undoing create \n” );

}

else if ( (!safeToOpen) && Scanned)
{
scannerContext->AlreadyScanned=TRUE;
status = FltSetStreamContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
scannerContext,
NULL );
if (!NT_SUCCESS(status))
{
FltReleaseContext(scannerContext);
FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);
}

// Ask the filter manager to undo the create.
DbgPrint( “!!! scanner.sys --virus detected in postcreate !!!\n” );
DbgPrint( “!!! scanner.sys – undoing create \n” );

FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject );

Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;

returnStatus = FLT_POSTOP_FINISHED_PROCESSING;

}
else if (FltObjects->FileObject->WriteAccess)
{
DbgPrint( “!!! scanner.sys – No foul language in the file \n” );

// The create has requested write access, mark to rescan the file.
// Allocate the context.
scannerContext->AlreadyScanned=TRUE;
// scannerContext->RescanRequired = TRUE;
status = FltSetStreamContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
scannerContext,
NULL );
if (!NT_SUCCESS(status))
{
FltReleaseContext(scannerContext);
FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);
}

}//if
else
{
scannerContext->AlreadyScanned=TRUE;
status = FltSetStreamContext( FltObjects->Instance,
FltObjects->FileObject,
FLT_SET_CONTEXT_KEEP_IF_EXISTS,
scannerContext,
NULL );
if (!NT_SUCCESS(status))
{
FltReleaseContext(scannerContext);
FltReleaseFileNameInformation(Name);
if (scannerContext != NULL)
FltReleaseContext(scannerContext);
}
}

}//AlreadyScanned

if(scannerContext != NULL)
FltReleaseContext(scannerContext);

FltReleaseFileNameInformation(Name);

return returnStatus;

}

I found this discussion which I think will be useful to help with the reference count issue.
http://www.osronline.com/showthread.cfm?link=170321