Develop File System Mini Filter Driver Step By Step

minispy Minifilter Sample

Description

The minispy sample is a tool to monitor and log any I/O and transaction activity that occurs in the system. This sample is similar to the FileSpy legacy filter; however, unlike FileSpy, minispy has been implemented as a minifilter.

Theory of Operation

minispy consists of both user-mode and kernel-mode components. The kernel-mode component registers callback functions that correspond to various I/O and transaction operations with the filter manager. These callback functions help minispy record any I/O and transaction activity occurring in the system. When a user can request the recorded information, the recorded information is passed to the user-mode component, which can either output it on screen or log it to a file on disk.

To observe I/O activity on a device, you must explicitly attach minispy to that device by using the minispy user-mode component. Similarly, you can request minispy to stop logging data for a particular device.

Implementation and Design

You should use this sample if you are developing a minifilter.

Building the Sample

  • 1. Open the appropriate WDK free or check build environment to set basic environment variables that the build utility needs.
  • 2. Navigate to the directory that contains the device source code (for example, CD src\filesys\miniFilter\minispy).
  • 3. Run build -ceZor use the BCZ macro. This behavior calls the Microsoft make routines that produce log files called Buildxxx_yyy_zzz.log, Buildxx_yyy_zzz.wrn, and Buildxxx_yyy_zzz.errif there are any warnings or errors. xxx stands for "fre" or "chk" depending on the environment you choose, yyystands for the operating system version (for example, "Wlh" for Windows Vista), and zzzstands for the platform version (for example, "x86" for x86-based, "IA64" for Itanium-based, or "AMD64" for x64-based).

If the build succeeds, the driver, minispy.sys, will be placed in a platform-specific subdirectory of your %TargetPath% directory that is specified in the sources file.

Installation

End User

The minispy minifilter comes with an INF file that will install the minifilter. To install the minifilter, do the following:

  • 1. Make sure that minispy.exe, minspy.sys,and minispy.inf are in the same directory.
  • 2. In Windows Explorer, right-click minispy.inf, and click Install.

This installation will make the necessary registry updates to register the metadata service and place minispy.sys in the %SystemRoot%\system32\drivers directory.

To load this minifilter, run fltmc load minispy or net start minispy.

 

Writing a DriverEntry Routine for a Minifilter Driver

Every file system minifilter driver must have a DriverEntry routine. The DriverEntry routine is called when the minifilter driver is loaded.

The DriverEntry routine performs global initialization, registers the minifilter driver, and initiates filtering. This routine runs in a system thread context at IRQL PASSIVE_LEVEL.

The DriverEntry routine is defined as follows:

NTSTATUS
(*PDRIVER_INITIALIZE) (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

DriverEntry has two input parameters. The first, DriverObject, is the driver object that was created when the minifilter driver was loaded. The second, RegistryPath, is a pointer to a counted Unicode string that contains a path to the minifilter driver's registry key.

A minifilter driver's DriverEntry routine must perform the following steps, in order:

  • 1. Perform any needed global initialization for the minifilter driver.
  • 2. Register the minifilter driver by calling FltRegisterFilter.
  • 3. Initiate filtering by calling FltStartFiltering.
  • 4. Return an appropriate NTSTATUS value.

Registering the Minifilter Driver

Every minifilter driver must call FltRegisterFilter from its DriverEntry routine to add itself to the global list of registered minifilter drivers and to provide the filter manager with a list of callback routines and other information about the driver.

In the MiniSpy sample, the minifilter driver is registered as shown in the following code example:

NTSTATUS status;
status = FltRegisterFilter(
DriverObject, //Driver
&FilterRegistration, //Registration
&MiniSpyData.FilterHandle); //RetFilter

FltRegisterFilter has two input parameters. The first, Driver, is the driver object pointer that the minifilter driver received as the DriverObject input parameter to its DriverEntry routine. The second, Registration, is a pointer to an FLT_REGISTRATION structure that contains entry points to the minifilter driver's callback routines.

In addition, FltRegisterFilter has an output parameter, RetFilter, that receives an opaque filter pointer for the minifilter driver. This filter pointer is a required input parameter for many FltXxx support routines, including FltStartFiltering and FltUnregisterFilter.

Initiating Filtering

After calling FltRegisterFilter, a minifilter driver's DriverEntry routine typically calls FltStartFiltering to begin filtering I/O operations.

Every minifilter driver must call FltStartFiltering from its DriverEntry routine to notify the filter manager that the minifilter driver is ready to begin attaching to volumes and filtering I/O requests. After the minifilter driver calls FltStartFiltering, the filter manager treats the minifilter driver as a fully active minifilter driver, presenting it with I/O requests and notifications of volumes to attach to. The minifilter driver must be prepared to begin receiving these I/O requests and notifications even before FltStartFiltering returns.

In the MiniSpy sample driver, FltStartFiltering is called as shown in the following code example:

status = FltStartFiltering( MiniSpyData.FilterHandle );
if( !NT_SUCCESS( status )) {
FltUnregisterFilter( MiniSpyData.FilterHandle );
}

If the call to FltStartFiltering does not return STATUS_SUCCESS, the minifilter driver must call FltUnregisterFilter to unregister itself.

Returning Status from a Minifilter DriverEntry Routine

A minifilter driver's DriverEntry routine normally returns STATUS_SUCCESS. But if minifilter initialization fails, the DriverEntry routine should return an appropriate error NTSTATUS value.

If the DriverEntry routine returns a status value that is not a success NTSTATUS value, the system responds by unloading the minifilter driver. The minifilter driver's FilterUnloadCallback routine is not called. For this reason, the DriverEntry routine must free any memory that was allocated for system resources before returning a status value that is not a success NTSTATUS value.

 

 

 

//---------------------------------------------------------------------------

//                      ROUTINES

//---------------------------------------------------------------------------

 

NTSTATUS

DriverEntry (

    __in PDRIVER_OBJECT DriverObject,

    __in PUNICODE_STRING RegistryPath

    )

/*++

 

Routine Description:

 

    This routine is called when a driver first loads.  Its purpose is to

    initialize global state and then register with FltMgr to start filtering.

 

Arguments:

 

    DriverObject - Pointer to driver object created by the system to

        represent this driver.

    RegistryPath - Unicode string identifying where the parameters for this

        driver are located in the registry.

 

Return Value:

 

    Status of the operation.

 

--*/

{

    PSECURITY_DESCRIPTOR sd;

    OBJECT_ATTRIBUTES oa;

    UNICODE_STRING uniString;

    NTSTATUS status = STATUS_SUCCESS;

 

    try {

 

        //

        // Initialize global data structures.

        //

 

        MiniSpyData.LogSequenceNumber = 0;

        MiniSpyData.MaxRecordsToAllocate = DEFAULT_MAX_RECORDS_TO_ALLOCATE;

        MiniSpyData.RecordsAllocated = 0;

        MiniSpyData.NameQueryMethod = DEFAULT_NAME_QUERY_METHOD;

 

        MiniSpyData.DriverObject = DriverObject;

 

        InitializeListHead( &MiniSpyData.OutputBufferList );

        KeInitializeSpinLock( &MiniSpyData.OutputBufferLock );

 

        ExInitializeNPagedLookasideList( &MiniSpyData.FreeBufferList,

                                         NULL,

                                         NULL,

                                         0,

                                         RECORD_SIZE,

                                         SPY_TAG,

                                         0 );

 

#if MINISPY_VISTA

 

        //

        //  Dynamically import FilterMgr APIs for transaction support

        //

 

#pragma warning(push)

#pragma warning(disable:4055) // type cast from data pointer to function pointer

        MiniSpyData.PFltSetTransactionContext = (PFLT_SET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltSetTransactionContext" );

        MiniSpyData.PFltGetTransactionContext = (PFLT_GET_TRANSACTION_CONTEXT) FltGetRoutineAddress( "FltGetTransactionContext" );

        MiniSpyData.PFltEnlistInTransaction = (PFLT_ENLIST_IN_TRANSACTION) FltGetRoutineAddress( "FltEnlistInTransaction" );

#pragma warning(pop)

 

#endif

 

        //

        // Read the custom parameters for MiniSpy from the registry

        //

 

        SpyReadDriverParameters(RegistryPath);

 

        //

        //  Now that our global configuration is complete, register with FltMgr.

        //

 

        status = FltRegisterFilter( DriverObject,

                                    &FilterRegistration,

                                    &MiniSpyData.Filter );

 

        if (!NT_SUCCESS( status )) {

 

           leave;

        }

 

 

        status  = FltBuildDefaultSecurityDescriptor( &sd,

                                                     FLT_PORT_ALL_ACCESS );

 

        if (!NT_SUCCESS( status )) {

            leave;

        }

 

        RtlInitUnicodeString( &uniString, MINISPY_PORT_NAME );

 

        InitializeObjectAttributes( &oa,

                                    &uniString,

                                    OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,

                                    NULL,

                                    sd );

 

        status = FltCreateCommunicationPort( MiniSpyData.Filter,

                                             &MiniSpyData.ServerPort,

                                             &oa,

                                             NULL,

                                             SpyConnect,

                                             SpyDisconnect,

                                             SpyMessage,

                                             1 );

 

        FltFreeSecurityDescriptor( sd );

 

        if (!NT_SUCCESS( status )) {

            leave;

        }

 

        //

        //  We are now ready to start filtering

        //

 

        status = FltStartFiltering( MiniSpyData.Filter );

 

    } finally {

 

        if (!NT_SUCCESS( status ) ) {

 

             if (NULL != MiniSpyData.ServerPort) {

                 FltCloseCommunicationPort( MiniSpyData.ServerPort );

             }

 

             if (NULL != MiniSpyData.Filter) {

                 FltUnregisterFilter( MiniSpyData.Filter );

             }

 

             ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList );

        }

    }

 

    return status;

}

Writing a FilterUnloadCallback Routine for a Minifilter Driver

A file system minifilter driver can optionally register a PFLT_FILTER_UNLOAD_CALLBACK -typed routine as the minifilter driver's FilterUnloadCallback routine. This callback routine is also referred to as the minifilter driver's unload routine.

Minifilter drivers are not required to register a FilterUnloadCallback routine. However, we strongly recommend that a minifilter driver registers this callback routine, because if a minifilter driver does not register a FilterUnloadCallback routine, the driver cannot be unloaded.

To register this callback routine, the minifilter driver stores the address of a PFLT_FILTER_UNLOAD_CALLBACK-typed routine in the FilterUnloadCallback member of the FLT_REGISTRATION structure that the minifilter driver passes as a parameter to FltRegisterFilter in its DriverEntry routine.

The FilterUnloadCallback routine is defined as follows:

typedef NTSTATUS
(*PFLT_FILTER_UNLOAD_CALLBACK) (
FLT_FILTER_UNLOAD_FLAGS Flags
);

The FilterUnloadCallback routine has one input parameter, Flags, which can be NULL or FLTFL_FILTER_UNLOAD_MANDATORY. The filter manager sets this parameter to FLTFL_FILTER_UNLOAD_MANDATORY to indicate that the unload operation is mandatory. For more information about this parameter, see PFLT_FILTER_UNLOAD_CALLBACK .

A minifilter driver's FilterUnloadCallback routine must perform the following steps:

  • ·         Close any open kernel-mode communication server port handles.
  • ·         Call FltUnregisterFilter to unregister the minifilter driver.
  • ·         Perform any needed global cleanup.
  • ·         Return an appropriate NTSTATUS value.

Closing the Communication Server Port

If the minifilter driver previously opened a kernel-mode communication server port by calling FltCreateCommunicationPort , it must close the port by calling FltCloseCommunicationPort . To prevent the system from hanging during the unload process, the minifilter driver's FilterUnloadCallback routine must close this port before calling FltUnregisterFilter .

If a user-mode application has an open connection to the communication server port, any client port for that connection will remain open after FltCloseCommunicationPort returns. However, the filter manager will close any client ports when the minifilter driver is unloaded.

Unregistering the Minifilter

A minifilter driver's FilterUnloadCallback routine must call FltUnregisterFilter to unregister the minifilter driver. Calling FltUnregisterFilter causes the following things to happen:

  • ·         The minifilter driver's callback routines are unregistered.
  • ·         The minifilter driver's instances are torn down, and the minifilter driver's InstanceTeardownStartCallback and InstanceTeardownCompleteCallback routines are called for each minifilter driver instance.
  • ·         If the minifilter driver set any contexts on volumes, instances, streams, or stream handles, these contexts are deleted. If the minifilter driver has registered a CleanupContext callback routine for a given context type, the filter manager calls the CleanupContext routine before deleting the context.

If there are outstanding rundown references on the minifilter driver's opaque filter pointer, FltUnregisterFilter enters a wait state until they are removed. Outstanding rundown references usually happen because the minifilter driver has called FltQueueGenericWorkItem to insert a work item into a system work queue, and the work item has not yet been dequeued and processed. (The filter manager adds the rundown reference when the minifilter driver calls FltQueueGenericWorkItem and removes it when the minifilter driver's work routine returns.)

Outstanding rundown references can also happen if the minifilter driver has called any routines that add a rundown reference to the minifilter driver's opaque filter pointer, such as FltObjectReference or FltGetFilterFromInstance , but did not subsequently call FltObjectDereference .

Performing Global Cleanup

A minifilter driver's FilterUnloadCallback routine must perform any needed global cleanup. The following list includes examples of global cleanup tasks that a minifilter driver might perform:

  • ·         Call ExDeleteResourceLite to delete a global resource variable that was initialized by a previous call to ExInitializeResourceLite .
  • ·         Call ExFreePool or ExFreePoolWithTag to free global memory that was allocated by a previous call to a routine such as ExAllocatePoolWithTag .
  • ·         Call ExDeleteNPagedLookasideList or ExDeletePagedLookasideList to delete a lookaside list that was allocated by a previous call to ExInitializeNPagedLookasideList or ExInitializePagedLookasideList , respectively.
  • ·         Call PsRemoveCreateThreadNotifyRoutine or PsRemoveLoadImageNotifyRoutine to unregister a global callback routine that was registered by a previous call to PsSetCreateThreadNotifyRoutine or PsSetLoadImageNotifyRoutine , respectively.

Returning Status from a FilterUnloadCallback Routine

A minifilter driver's FilterUnloadCallback routine normally returns STATUS_SUCCESS.

To refuse an unload operation that is not mandatory, the minifilter driver should return an appropriate warning or error NTSTATUS value such as STATUS_FLT_DO_NOT_DETACH. For more information about mandatory unload operations, see Writing a FilterUnloadCallback Routine and PFLT_FILTER_UNLOAD_CALLBACK .

If the FilterUnloadCallback routine returns a warning or error NTSTATUS value and the unload operation is not mandatory, the minifilter driver will not be unloaded.

NTSTATUS

SpyFilterUnload (

    __in FLT_FILTER_UNLOAD_FLAGS Flags

    )

/*++

 

Routine Description:

 

    This is called when a request has been made to unload the filter.  Unload

    requests from the Operation System (ex: "sc stop minispy" can not be

    failed.  Other unload requests may be failed.

 

    You can disallow OS unload request by setting the

    FLTREGFL_DO_NOT_SUPPORT_SERVICE_STOP flag in the FLT_REGISTARTION

    structure.

 

Arguments:

 

    Flags - Flags pertinent to this operation

 

Return Value:

 

    Always success

 

--*/

{

    UNREFERENCED_PARAMETER( Flags );

 

    PAGED_CODE();

 

    //

    //  Close the server port. This will stop new connections.

    //

 

    FltCloseCommunicationPort( MiniSpyData.ServerPort );

 

    FltUnregisterFilter( MiniSpyData.Filter );

 

    SpyEmptyOutputBufferList();

    ExDeleteNPagedLookasideList( &MiniSpyData.FreeBufferList );

 

    return STATUS_SUCCESS;

}

 

Writing Preoperation and Postoperation Callback Routines

In its DriverEntry routine, a minifilter driver can register up to one preoperation callback routine and up to one postoperation callback routine for each type of I/O operation that it needs to filter.

Unlike a legacy file system filter driver, a minifilter driver can choose which types of I/O operations to filter. A minifilter driver can register a preoperation callback routine for a given type of I/O operation without registering a postoperation callback, and vice versa. The minifilter driver receives only those I/O operations for which it has registered a preoperation or postoperation callback routine.

A preoperation callback routine is similar to a dispatch routine in the legacy filter driver model. When the filter manager processes an I/O operation, it calls the preoperation callback routine of each minifilter driver in the minifilter driver instance stack that has registered one for this type of I/O operation. The topmost minifilter driver in the stack—that is, the one whose instance has the highest altitude—receives the operation first. When that minifilter driver finishes processing the operation, it returns the operation to the filter manager, which then passes the operation to the next-highest minifilter driver, and so on. When all minifilter drivers in the minifilter driver instance stack have processed the I/O operation—unless a minifilter driver has completed the I/O operation—the filter manager sends the operation to legacy filters and the file system.

A postoperation callback routine is similar to a completion routine in the legacy filter driver model. Completion processing for an I/O operation begins when the I/O manager passes the operation to the file system and legacy filters that have registered completion routines for the operation. After these completion routines have finished, the filter manager performs completion processing for the operation. The filter manager then calls the postoperation callback routine of each minifilter driver in the minifilter driver instance stack that has registered one for this type of I/O operation. The bottom minifilter driver in the stack—that is, the one whose instance has the lowest altitude—receives the operation first. When that minifilter driver finishes processing the operation, it returns it to the filter manager, which then passes the operation to the next-lowest minifilter driver, and so on.

Registering Preoperation and Postoperation Callback Routines

To register preoperation callback routines and postoperation callback routines , a minifilter driver makes a single call to FltRegisterFilter in its DriverEntry routine. For the Registration parameter in FltRegisterFilter, the minifilter driver passes a pointer to an FLT_REGISTRATION structure. The OperationRegistration member of this structure contains a pointer to an array of FLT_OPERATION_REGISTRATION structures, one for each type of I/O operation that the minifilter driver must filter.

Each FLT_OPERATION_REGISTRATION structure in the array, except for the last one, contains the following information:

  • ·         The major function code for the operation
  • ·         For read and write operations (IRP_MJ_READ and IRP_MJ_WRITE), a set of flags that specify whether to ignore cached I/O or paging I/O or both for IRP-based I/O operations
  • ·         Entry points for up to one preoperation callback routine and one postoperation callback routine

The last element in the array must be {IRP_MJ_OPERATION_END}.

The following code example, which is taken from the Scanner sample minifilter driver, shows an array of FLT_OPERATION_REGISTRATION structures. The Scanner sample minifilter driver registers preoperation and postoperation callback routines for IRP_MJ_CREATE and preoperation callback routines for IRP_MJ_CLEANUP and IRP_MJ_WRITE operations.

const FLT_OPERATION_REGISTRATION Callbacks[] = {
{IRP_MJ_CREATE,
0,
ScannerPreCreate,
ScannerPostCreate},
{IRP_MJ_CLEANUP,
0,
ScannerPreCleanup,
NULL},
{IRP_MJ_WRITE,
0,
ScannerPreWrite,
NULL},
{IRP_MJ_OPERATION_END}
};

Filtering I/O Operations in a Minifilter Driver

The following list describes several guidelines for filtering specific types of I/O operations in a file system minifilter driver:

  • ·         The preoperation callback routine for IRP_MJ_CREATE cannot query or set contexts for files, streams, or stream handles, because, at pre-create time, the file or stream (if any) that is going to be created has not yet been determined.
  • ·         The postoperation callback routine for IRP_MJ_CLOSE cannot set or query contexts for files, streams, or stream handles, because the system-internal structures that those items are associated with are freed before the post-close routine is called.
  • ·         Minifilter drivers must never fail IRP_MJ_CLEANUP or IRP_MJ_CLOSE operations. These operations can be pended, returned to the filter manager, or completed with STATUS_SUCCESS. However, a preoperation callback routine must never fail these operations.
  • ·         Minifilter drivers cannot register a postoperation callback routine for IRP_MJ_SHUTDOWN.

Writing Preoperation Callback Routines

A file system minifilter driver uses one or more preoperation callback routines to filter I/O operations. Preoperation callback routines are similar to the dispatch routines that are used in legacy file system filter drivers.

A minifilter driver registers a preoperation callback routine for a particular type of I/O operation by storing the callback routine's entry point in the OperationRegistration member of the FLT_REGISTRATION structure that the minifilter driver passes as a parameter to FltRegisterFilter in its DriverEntry routine.

Minifilter drivers receive only those types of I/O operations for which they have registered a preoperation or postoperation callback routine. A minifilter driver can register a preoperation callback routine for a given type of I/O operation without registering a postoperation callback routine , and vice versa.

Every preoperation callback routine is defined as follows:

typedef FLT_PREOP_CALLBACK_STATUS
(*PFLT_PRE_OPERATION_CALLBACK) (
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
OUT PVOID *CompletionContext
);

Like a dispatch routine, a preoperation callback routine can be called at IRQL = PASSIVE_LEVEL or at IRQL = APC_LEVEL. Typically it is called at IRQL = PASSIVE_LEVEL, in the context of the thread that originated the I/O request. For fast I/O and file system filter (FsFilter) operations, the preoperation callback routine is always called at IRQL = PASSIVE_LEVEL. However, for an IRP-based operation, a minifilter driver's preoperation callback routine can be called in the context of a system worker thread if a higher filter or minifilter driver pends the operation for processing by the worker thread.

When the filter manager calls a minifilter driver's preoperation callback routine for a given I/O operation, the minifilter driver temporarily controls the I/O operation. The minifilter driver retains this control until it does one of the following:

  • ·         Returns a status value other than FLT_PREOP_PENDING from the preoperation callback routine.
  • ·         Calls FltCompletePendedPreOperation from a work routine that has processed an operation that was pended in the preoperation callback routine.

Passing an I/O Operation Down the Minifilter Driver Instance Stack

When a minifilter driver's preoperation callback routine or work routine returns an I/O operation to the filter manager, the filter manager sends the operation to minifilter drivers below the current minifilter driver in the minifilter driver instance stack and to legacy filters and the file system for further processing.

A minifilter driver's preoperation callback routine returns an I/O operation to the filter manager for further processing by returning one of the following status values:

  • ·         FLT_PREOP_SUCCESS_NO_CALLBACK (all operation types)
  • ·         FLT_PREOP_SUCCESS_WITH_CALLBACK (all operation types)
  • ·         FLT_PREOP_SYNCHRONIZE (IRP-based I/O operations only)

Note Although FLT_PREOP_SYNCHRONIZE should be returned only for IRP-based I/O operations, you can return this status value for other operation types. If it is returned for an I/O operation that is not an IRP-based I/O operation, the filter manager treats this return value as if it were FLT_PREOP_SUCCESS_WITH_CALLBACK.

Alternatively, the work routine for an operation that was pended in a preoperation callback routine returns an I/O operation to the filter manager by passing one of the preceding status values in the CallbackStatus parameter when it calls FltCompletePendedPreOperation to resume processing for the pended I/O operation.

Returning FLT_PREOP_SUCCESS_WITH_CALLBACK

If a minifilter driver's preoperation callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK, the filter manager calls the minifilter driver's postoperation callback routine during I/O completion.

Note If the minifilter driver's preoperation callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK but the minifilter driver has not registered a postoperation callback routine for the operation, the system asserts on a checked build.

If the minifilter driver's preoperation callback routine returns FLT_PREOP_SUCCESS_WITH_CALLBACK, it can return a non-NULL value in its CompletionContext output parameter. This parameter is an optional context pointer that is passed to the corresponding postoperation callback routine. The postoperation callback routine receives this pointer in its CompletionContext input parameter.

The FLT_PREOP_SUCCESS_WITH_CALLBACK status value can be returned for all types of I/O operations.

Returning FLT_PREOP_SUCCESS_NO_CALLBACK

If a minifilter driver's preoperation callback routine returns FLT_PREOP_SUCCESS_NO_CALLBACK, the filter manager does not call the minifilter driver's postoperation callback routine , if one exists, during I/O completion.

If the minifilter driver's preoperation callback routine returns FLT_PREOP_SUCCESS_NO_CALLBACK, it must return NULL in its CompletionContext output parameter.

The FLT_PREOP_SUCCESS_NO_CALLBACK status value can be returned for all types of I/O operations.

Returning FLT_PREOP_SYNCHRONIZE

If a minifilter driver's preoperation callback routine synchronizes an I/O operation by returning FLT_PREOP_SYNCHRONIZE, the filter manager calls the minifilter driver's postoperation callback routine during I/O completion.

The filter manager calls the minifilter driver's postoperation callback routine in the same thread context as the preoperation callback, at IRQL <= APC_LEVEL. (Note that this thread context is not necessarily the context of the originating thread.)

Note If the minifilter driver's preoperation callback routine returns FLT_PREOP_SYNCHRONIZE, but the minifilter driver has not registered a postoperation callback routine for the operation, the system asserts on a checked build.

If the minifilter driver's preoperation callback routine returns FLT_PREOP_SYNCHRONIZE, it can return a non-NULL value in its CompletionContext output parameter. This parameter is an optional context pointer that is passed to the corresponding postoperation callback routine. The postoperation callback routine receives this pointer in its CompletionContext input parameter.

A minifilter driver's preoperation callback routine should return FLT_PREOP_SYNCHRONIZE only for IRP-based I/O operations. However, this status value can be returned for other operation types. If it is returned for an I/O operation that is not an IRP-based I/O operation, the filter manager treats this return value as if it were FLT_PREOP_SUCCESS_WITH_CALLBACK. To determine whether an operation is an IRP-based I/O operation, use the FLT_IS_IRP_OPERATION macro.

Minifilter drivers should not return FLT_PREOP_SYNCHRONIZE for create operations, because these operations are already synchronized by the filter manager. If a minifilter driver has registered preoperation and postoperation callback routines for IRP_MJ_CREATE operations, the post-create callback routine is called at IRQL = PASSIVE_LEVEL, in the same thread context as the pre-create callback routine.

Minifilter drivers must never return FLT_PREOP_SYNCHRONIZE for asynchronous read or write operations. Doing so can severely degrade both minifilter driver and system performance and can even cause deadlocks if, for example, the modified page writer thread is blocked. Before returning FLT_PREOP_SYNCHRONIZE for an IRP-based read or write operation, a minifilter driver should verify that the operation is synchronous by calling FltIsOperationSynchronous .

The following types of I/O operations cannot be synchronized:

  • ·         Oplock file system control (FSCTL) operations (MajorFunction is IRP_MJ_FILE_SYSTEM_CONTROL; FsControlCode is FSCTL_REQUEST_FILTER_OPLOCK , FSCTL_REQUEST_BATCH_OPLOCK , FSCTL_REQUEST_OPLOCK_LEVEL_1 , or FSCTL_REQUEST_OPLOCK_LEVEL_2 .)
  • ·         Notify change directory operations (MajorFunction is IRP_MJ_DIRECTORY_CONTROL; MinorFunction is IRP_MN_NOTIFY_CHANGE_DIRECTORY.)
  • ·         Byte-range lock requests (MajorFunction is IRP_MJ_LOCK_CONTROL; MinorFunction is IRP_MN_LOCK.)

FLT_PREOP_SYNCHRONIZE cannot be returned for any of these operations.

Completing an I/O Operation in a Preoperation Callback Routine

To complete an I/O operation means to halt processing for the operation, assign it a final NTSTATUS value, and return it to the filter manager.

When a minifilter driver completes an I/O operation, the filter manager does the following:

  • ·         Does not send the operation to minifilter drivers below the current minifilter driver, to legacy filters, or to the file system.
  • ·         Calls the postoperation callback routines of the minifilter drivers above the current minifilter driver in the minifilter driver instance stack.
  • ·         Does not call the current minifilter driver's postoperation callback routine for the operation, if one exists.

A minifilter driver's preoperation callback routine completes an I/O operation by performing the following steps:

  • 1. Setting the callback data structure's IoStatus.Status field to the final NTSTATUS value for the operation.
  • 2. Returning FLT_PREOP_COMPLETE.

A preoperation callback routine that completes an I/O operation cannot set a non-NULL completion context (in the CompletionContext output parameter).

A minifilter driver can also complete an operation in the work routine for a previously pended I/O operation by performing the following steps:

  • 1. Setting the callback data structure's IoStatus.Status field to the final NTSTATUS value for the operation.
  • 2. Passing FLT_PREOP_COMPLETE in the CallbackStatus parameter when the work routine calls FltCompletePendedPreOperation .

When completing an I/O operation, a minifilter driver must set the callback data structure's IoStatus.Status field to the final NTSTATUS value for the operation, but this NTSTATUS value cannot be STATUS_PENDING or STATUS_FLT_DISALLOW_FAST_IO. For a cleanup or close operation, the field must be STATUS_SUCCESS. These operations cannot be completed with any other NTSTATUS value.

Completing an I/O operation is often referred to as succeeding or failing the operation, depending on the NTSTATUS value:

  • ·         To succeed an I/O operation means to complete it with a success or informational NTSTATUS value, such as STATUS_SUCCESS.
  • ·         To fail an I/O operation means to complete it with an error or warning NTSTATUS value, such as STATUS_INVALID_DEVICE_REQUEST or STATUS_BUFFER_OVERFLOW.

NTSTATUS values are defined in ntstatus.h. These values fall into four categories: success, informational, warning, and error.

Disallowing a Fast I/O Operation in a Preoperation Callback Routine

In certain circumstances, a minifilter driver might choose to disallow a fast I/O operation instead of completing it. Disallowing a fast I/O operation prevents the fast I/O path from being used for the operation.

Like completing an I/O operation, disallowing a fast I/O operation means to halt processing on it and return it to the filter manager. However, disallowing a fast I/O operation is different from completing it. If a minifilter driver disallows a fast I/O operation that was issued by the I/O manager, the I/O manager may reissue the same operation as an equivalent IRP-based operation.

When a minifilter driver's preoperation callback routine disallows a fast I/O operation, the filter manager does the following:

  • ·         Does not send the operation to minifilter drivers below the current minifilter driver, to legacy filters, or to the file system.
  • ·         Calls the postoperation callback routines of the minifilter drivers above the current minifilter driver in the minifilter driver instance stack.
  • ·         Does not call the current minifilter driver's postoperation callback routine for the operation, if one exists.

A minifilter driver disallows a fast I/O operation by returning FLT_PREOP_DISALLOW_FASTIO from the preoperation callback routine for the operation.

The preoperation callback routine should not set the callback data structure's IoStatus.Status field, because the filter manager automatically sets this field to STATUS_FLT_DISALLOW_FAST_IO.

FLT_PREOP_DISALLOW_FASTIO can only be returned for fast I/O operations. To determine whether an operation is a fast I/O operation, see FLT_IS_FASTIO_OPERATION .

Minifilter drivers cannot return FLT_PREOP_DISALLOW_FASTIO for IRP_MJ_SHUTDOWN, IRP_MJ_VOLUME_MOUNT, or IRP_MJ_VOLUME_DISMOUNT operations.

Pending an I/O Operation in a Preoperation Callback Routine

A minifilter driver's preoperation callback routine can pend an I/O operation by posting the operation to a system work queue and returning FLT_PREOP_PENDING. Returning this status value indicates that the minifilter driver is retaining control of the I/O operation until it calls FltCompletePendedPreOperation to resume processing for the I/O operation.

A minifilter driver's preoperation callback routine pends an I/O operation by performing the following steps:

  • 1. Posting the I/O operation to a system work queue by calling a routine such as FltQueueDeferredIoWorkItem .
  • 2. Returning FLT_PREOP_PENDING.

A minifilter driver that must pend all (or most) incoming I/O operations should not use routines such as FltQueueDeferredIoWorkItem to pend operations, because calling this routine can cause the system work queues to be flooded. Instead, such a minifilter driver should use a cancel-safe queue. For more information about using cancel-safe queues, see FltCbdqInitialize .

Note that the call to FltQueueDeferredIoWorkItem will fail if any of the following conditions are true:

  • ·         The operation is not an IRP-based I/O operation.
  • ·         The operation is a paging I/O operation.
  • ·         The TopLevelIrp field of the current thread is not NULL. (For more information about how to find the value of this field, see IoGetTopLevelIrp .)
  • ·         The target instance for the I/O operation is being torn down.

If the minifilter driver's preoperation callback routine returns FLT_PREOP_PENDING, it must return NULL in the CompletionContext output parameter.

A minifilter driver can return FLT_PREOP_PENDING only for IRP-based I/O operations. To determine whether an operation is an IRP-based I/O operation, use the FLT_IS_IRP_OPERATION macro.

The work routine that dequeues and processes the I/O operation must call FltCompletePendedPreOperation to resume processing for the operation.

Writing Postoperation Callback Routines

A file system minifilter driver uses one or more postoperation callback routines to filter I/O operations.

Postoperation callback routines are similar to the completion routines that are used in legacy file system filter drivers.

A minifilter driver registers a postoperation callback routine for a particular type of I/O operation in the same way it registers a preoperation callback routine —that is, by storing the callback routine's entry point in the OperationRegistration member of the FLT_REGISTRATION structure that the minifilter driver passes as a parameter to FltRegisterFilter in its DriverEntry routine.

Minifilter drivers receive only those types of I/O operations for which they have registered a preoperation or postoperation callback routine. A minifilter driver can register a preoperation callback routine for a given type of I/O operation without registering a postoperation callback, and vice versa.

Every postoperation callback routine is defined as follows:

typedef FLT_POSTOP_CALLBACK_STATUS
(*PFLT_POST_OPERATION_CALLBACK) (
IN OUT PFLT_CALLBACK_DATA Data,
IN PCFLT_RELATED_OBJECTS FltObjects,
IN PVOID CompletionContext,
IN FLT_POST_OPERATION_FLAGS Flags
);

Like a completion routine, a postoperation callback routine is called at IRQL <= DISPATCH_LEVEL, in an arbitrary thread context.

Because it can be called at IRQL = DISPATCH_LEVEL, a postoperation callback routine cannot call kernel-mode routines that must be called at a lower IRQL, such as FltLockUserBuffer or RtlCompareUnicodeString . For the same reason, any data structures that are used in a postoperation callback routine must be allocated from nonpaged pool.

The following situations are several exceptions to the preceding rule:

  • ·         If a minifilter driver's preoperation callback routine returns FLT_PREOP_SYNCHRONIZE for an IRP-based I/O operation, the corresponding postoperation callback routine is called at IRQL <= APC_LEVEL, in the same thread context as the preoperation callback routine.
  • ·         The postoperation callback routine for a fast I/O operation is called at IRQL = PASSIVE_LEVEL, in the same thread context as the preoperation callback routine.
  • ·         Post-create callback routines are called at IRQL = PASSIVE_LEVEL, in the context of the thread that originated the IRP_MJ_CREATE operation.

When the filter manager calls a minifilter driver's postoperation callback routine for a given I/O operation, the minifilter driver temporarily controls the I/O operation. The minifilter driver retains this control until it does one of the following:

  • ·         Returns FLT_POSTOP_FINISHED_PROCESSING from the postoperation callback routine.
  • ·         Calls FltCompletePendedPostOperation from a work routine that has processed an IRP-based I/O operation that was pended in the postoperation callback routine.

Performing Completion Processing for an I/O Operation

A minifilter driver's postoperation callback routine is called when an I/O operation has been completed by the underlying file system, by a legacy filter, or by another minifilter driver that is at a lower altitude in the minifilter driver instance stack.

In addition, when a minifilter driver instance is being torn down, the filter manager "drains" any I/O operations for which the instance has received a preoperation callback and is awaiting a postoperation callback . In this situation, the filter manager calls the minifilter driver's postoperation callback routine, even if the I/O operation has not been completed, and sets the FLTFL_POST_OPERATION_DRAINING flag in the Flags input parameter.

When the FLTFL_POST_OPERATION_DRAINING flag is set, the minifilter driver must not perform normal completion processing. Instead, it should perform only necessary cleanup, such as freeing memory that the minifilter driver allocated for the CompletionContext parameter in its preoperation callback routine, and return FLT_POSTOP_FINISHED_PROCESSING.

Ensuring that Completion Processing is Performed at Safe IRQL

As noted in Writing Postoperation Callback Routines, the postoperation callback routine for an IRP-based I/O operation can be called at IRQL = DISPATCH_LEVEL, unless the minifilter driver's preoperation callback routine synchronized the operation by returning FLT_PREOP_SYNCHRONIZE or the operation is a create operation, which is inherently synchronous. (For more information about this return value, see Returning FLT_PREOP_SYNCHRONIZE.)

However, for IRP-based I/O operations that are not already synchronized, minifilter drivers can use to two techniques to ensure that completion processing is performed at IRQL <= APC_LEVEL.

The first technique is for the postoperation callback routine to pend the I/O operation until completion processing can be performed at IRQL <= APC_LEVEL. This technique is described in Pending an I/O Operation in a Postoperation Callback Routine.

The second technique is for the minifilter driver's postoperation callback routine to call FltDoCompletionProcessingWhenSafe . FltDoCompletionProcessingWhenSafe pends the I/O operation only if the current IRQL is >= DISPATCH_LEVEL. Otherwise, this routine executes the minifilter driver's SafePostCallback routine immediately. This technique is described in FltDoCompletionProcessingWhenSafe .

Pending an I/O Operation in a Postoperation Callback Routine

A minifilter driver's postoperation callback routine can pend an I/O operation by performing the following steps:

  • 1. Calling FltAllocateDeferredIoWorkItem to allocate a work item for the I/O operation.
  • 2. Calling FltQueueDeferredIoWorkItem to post the I/O operation to a system work queue.
  • 3. Returning FLT_POSTOP_MORE_PROCESSING_REQUIRED.

Note that the call to FltQueueDeferredIoWorkItem will fail if any of the following conditions are true:

  • ·         The operation is not an IRP-based I/O operation.
  • ·         The operation is a paging I/O operation.
  • ·         The TopLevelIrp field of the current thread is not NULL. (For more information about how to find the value of this field, see IoGetTopLevelIrp .)
  • ·         The target instance for the I/O operation is being torn down. (The filter manager indicates this situation by setting the FLTFL_POST_OPERATION_DRAINING flag in the Flags input parameter to the postoperation callback routine.)

Minifilter drivers must be prepared to handle this failure. If your minifilter driver cannot handle such failures, you should consider using the technique that is described in Returning FLT_PREOP_SYNCHRONIZE instead of pending the I/O operation.

After the minifilter driver's postoperation callback routine returns FLT_POSTOP_MORE_PROCESSING_REQUIRED, the filter manager will not perform any further completion processing for the I/O operation until the minifilter driver's work routine calls FltCompletePendedPostOperation to return control of the operation to the filter manager. The filter manager will not perform any further processing in this situation even if the work routine sets a failure NTSTATUS value in the IoStatus.Status field of the callback data structure for the operation.

The work routine that dequeues and performs completion processing for the I/O operation must call FltCompletePendedPostOperation to return control of the operation to the filter manager.

Failing an I/O Operation in a Postoperation Callback Routine

A minifilter driver's postoperation callback routine can fail a successful I/O operation, but simply failing an I/O operation does not undo the effect of the operation. The minifilter driver is responsible for performing any processing that is needed to undo the operation.

For example, a minifilter driver's post-create callback routine can fail a successful IRP_MJ_CREATE operation by performing the following steps:

  • 1. Calling FltCancelFileOpen to close the file that was created or opened by the create operation. Note that FltCancelFileOpen does not undo any modifications to the file. For example, FltCancelFileOpen does not delete a newly created file or restore a truncated file to its previous size.
  • 2. Setting the callback data structure's IoStatus.Status field to the final NTSTATUS value for the operation. This value must be a valid error NTSTATUS value, such as STATUS_ACCESS_DENIED.
  • 3. Setting the callback data structure's IoStatus.Information field to zero.
  • 4. Returning FLT_POSTOP_FINISHED_PROCESSING.

When setting the callback data structure's IoStatus.Status field to the final NTSTATUS value for the operation, the minifilter driver must specify a valid error NTSTATUS value. Note that minifilter drivers cannot specify STATUS_FLT_DISALLOW_FAST_IO; only the filter manager can use this NTSTATUS value.

Callers of FltCancelFileOpen must be running at IRQL <= APC_LEVEL. However, a minifilter driver can safely call this routine from a post-create callback routine, because, for IRP_MJ_CREATE operations, the postoperation callback routine is called at IRQL = PASSIVE_LEVEL, in the context of the thread that originated the create operation.

Modifying the Parameters for an I/O Operation

A minifilter driver can modify the parameters for an I/O operation. For example, a minifilter driver's preoperation callback routine can redirect an I/O operation to a different volume by changing the target instance for the operation. The new target instance must be an instance of the same minifilter driver at the same altitude on another volume.

The parameters for an I/O operation are found in the callback data ( FLT_CALLBACK_DATA ) structure and I/O parameter block ( FLT_IO_PARAMETER_BLOCK ) structure for the operation. The minifilter driver's preoperation callback routine and postoperation callback routine receive a pointer to the callback data structure for the operation in the Data input parameter. The Iopb member of the callback data structure is a pointer to an I/O parameter block structure that contains the parameters for the operation.

If a minifilter driver's preoperation callback routine modifies the parameters for an I/O operation, all minifilter drivers below that minifilter driver in the minifilter driver instance stack will receive the modified parameters in their preoperation and postoperation callback routines.

The modified parameters are not received by the current minifilter driver's postoperation callback routine or by any minifilter drivers above that minifilter driver in the minifilter driver instance stack. In all situations, a minifilter driver's preoperation and postoperation callback routines receive the same input parameter values for a given I/O operation.

After modifying the parameters for an I/O operation, the preoperation or postoperation callback routine must indicate that it has done so by calling FltSetCallbackDataDirty , unless it has changed the contents of the callback data structure's IoStatus field. Otherwise, the filter manager will ignore any changes to parameter values. FltSetCallbackDataDirty sets the FLTFL_CALLBACK_DATA_DIRTY flag in the callback data structure for the I/O operation. Minifilter drivers can test this flag by calling FltIsCallbackDataDirty or clear it by calling FltClearCallbackDataDirty .

If a minifilter driver's preoperation callback routine modifies the parameters for an I/O operation, all minifilter drivers below that minifilter driver in the minifilter driver instance stack will receive the modified parameters in the Data and FltObjects input parameters to their preoperation and postoperation callback routines. (Minifilter drivers cannot directly modify the contents of the FLT_RELATED_OBJECTS structure that is pointed to by the FltObjects parameter. However, if a minifilter driver modifies the target instance or target file object for an I/O operation, the filter manager modifies the value of the corresponding Instance or FileObject member of the FLT_RELATED_OBJECTS structure that is passed to lower minifilter drivers.)

Although any parameter changes that a minifilter driver's preoperation callback routine makes are not received by the minifilter driver's own postoperation callback routine, a preoperation callback routine is able to pass information about changed parameters to the minifilter driver's own postoperation callback routine. If the preoperation callback routine passes the I/O operation down the stack by returning FLT_PREOP_SUCCESS_WITH_CALLBACK or FLT_PREOP_SYNCHRONIZE, it can store information about changed parameter values into a minifilter driver–defined structure that is pointed to by the CompletionContext output parameter. The filter manager passes this structure pointer in the CompletionContext input parameter to the postoperation callback routine.

 

CONST FLT_OPERATION_REGISTRATION Callbacks[] = {

    { IRP_MJ_CREATE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_CREATE_NAMED_PIPE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_CLOSE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_READ,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_WRITE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_QUERY_INFORMATION,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_SET_INFORMATION,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_QUERY_EA,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_SET_EA,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_FLUSH_BUFFERS,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_QUERY_VOLUME_INFORMATION,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_SET_VOLUME_INFORMATION,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_DIRECTORY_CONTROL,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_FILE_SYSTEM_CONTROL,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_DEVICE_CONTROL,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_INTERNAL_DEVICE_CONTROL,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_SHUTDOWN,

      0,

      SpyPreOperationCallback,

      NULL },                           //post operation callback not supported

 

    { IRP_MJ_LOCK_CONTROL,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_CLEANUP,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_CREATE_MAILSLOT,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_QUERY_SECURITY,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_SET_SECURITY,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_QUERY_QUOTA,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_SET_QUOTA,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_PNP,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_RELEASE_FOR_SECTION_SYNCHRONIZATION,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_ACQUIRE_FOR_MOD_WRITE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_RELEASE_FOR_MOD_WRITE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_ACQUIRE_FOR_CC_FLUSH,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_RELEASE_FOR_CC_FLUSH,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

/*    { IRP_MJ_NOTIFY_STREAM_FILE_OBJECT,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },*/

 

    { IRP_MJ_FAST_IO_CHECK_IF_POSSIBLE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_NETWORK_QUERY_OPEN,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_MDL_READ,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_MDL_READ_COMPLETE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_PREPARE_MDL_WRITE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_MDL_WRITE_COMPLETE,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_VOLUME_MOUNT,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_VOLUME_DISMOUNT,

      0,

      SpyPreOperationCallback,

      SpyPostOperationCallback },

 

    { IRP_MJ_OPERATION_END }

};

 

const FLT_CONTEXT_REGISTRATION Contexts[] = {

 

#if MINISPY_VISTA

 

    { FLT_TRANSACTION_CONTEXT,

      0,

      SpyDeleteTxfContext,

      sizeof(MINISPY_TRANSACTION_CONTEXT),

      'ypsM' },

 

#endif // MINISPY_VISTA

 

    { FLT_CONTEXT_END }

};

 

//

//  This defines what we want to filter with FltMgr

//

 

CONST FLT_REGISTRATION FilterRegistration = {

 

    sizeof(FLT_REGISTRATION),               //  Size

    FLT_REGISTRATION_VERSION,               //  Version

    0,                                      //  Flags

 

    Contexts,                               //  Context

    Callbacks,                              //  Operation callbacks

 

    SpyFilterUnload,                        //  FilterUnload

 

    NULL,                                   //  InstanceSetup

    SpyQueryTeardown,                       //  InstanceQueryTeardown

    NULL,                                   //  InstanceTeardownStart

    NULL,                                   //  InstanceTeardownComplete

 

    NULL,                                   //  GenerateFileName

    NULL,                                   //  GenerateDestinationFileName

    NULL                                    //  NormalizeNameComponent

 

#if MINISPY_VISTA

 

    ,

    SpyKtmNotificationCallback              //  KTM notification callback

 

#endif // MINISPY_VISTA

 

};