Managing Contexts in a Minifilter Driver

Download EaseFilter Filter Driver SDK Setup File
Download EaseFilter Filter Driver SDK Zip File

A context is a structure that is defined by the minifilter driver and that can be associated with a filter manager object. Minifilter drivers can create and set contexts for the following objects:

·         Files (Windows Vista and later only.)

·         Instances

·         Volumes

·         Streams

·         Stream handles (file objects)

·         Transactions (Windows Vista and later only.)

Except for volume contexts, which must be allocated from nonpaged pool, contexts can be allocated from paged or nonpaged pool.

The filter manager deletes contexts automatically when the objects that they are attached to are deleted, when a minifilter driver instance is detached from a volume, or when the minifilter driver is unloaded.

Registering Context Types

When a minifilter driver calls FltRegisterFilter from its DriverEntry routine, it must register each type of context that it uses.

To register context types, the minifilter driver creates a variable-length array of FLT_CONTEXT_REGISTRATION structures and stores a pointer to the array in the ContextRegistration member of the FLT_REGISTRATION structure that the minifilter driver passes in the Registration parameter of FltRegisterFilter. The order of the elements in this array does not matter. However, the last element in the array must be {FLT_CONTEXT_END}.

For each context type that the minifilter driver uses, it must supply at least one context definition in the form of an FLT_CONTEXT_REGISTRATION structure. Each FLT_CONTEXT_REGISTRATION structure defines the type, size, and other information for the context.

When the minifilter driver creates a new context by calling FltAllocateContext, the filter manager uses the Size parameter of the FltAllocateContext routine, as well as the Size and Flags members of the FLT_CONTEXT_REGISTRATION structure, to select the context definition to be used.

For fixed-size contexts, the Size member of the FLT_CONTEXT_REGISTRATION structure specifies the size, in bytes, of the portion of the context stucture that is defined by the minifilter driver. The maximum size for a context is MAXUSHORT (64 KB). Zero is a valid size value. The filter manager implements fixed-size contexts using lookaside lists. The filter manager creates two lookaside lists for each size value: one paged and one nonpaged.

For variable-size contexts, the Size member must be set to FLT_VARIABLE_SIZED_CONTEXTS. The filter manager allocates variable-size contexts directly from paged or nonpaged pool.

In the Flags member of the FLT_CONTEXT_REGISTRATION structure, the FLTFL_CONTEXT_REGISTRATION_NO_EXACT_SIZE_MATCH flag can be specified. If the minifilter driver uses fixed-size contexts and this flag is specified, the filter manager allocates a context from the lookaside list if the context's size is greater than or equal to the requested size. Otherwise, the context's size must be equal to the requested size.

For a given context type, the minifilter driver can supply up to three fixed-size context definitions, each with a different size, and one variable-size definition. For more information, see FLT_CONTEXT_REGISTRATION.

The minifilter driver can optionally supply a context cleanup callback routine to be called before the context is freed. For more information, see PFLT_CONTEXT_CLEANUP_CALLBACK.

A minifilter driver can optionally define its own callback routines for allocating and freeing contexts, instead of relying on the filter manager to perform these tasks. However, this is very rarely necessary. For more information, see PFLT_CONTEXT_ALLOCATE_CALLBACK and PFLT_CONTEXT_FREE_CALLBACK.

The following code example, which is taken from the CTX sample minifilter driver, shows an array of FLT_CONTEXT_REGISTRATION structures that are used to register instance, file, stream, and file object (stream handle) contexts.

const FLT_CONTEXT_REGISTRATION contextRegistration[] = {
{ FLT_INSTANCE_CONTEXT, //ContextType
0, //Flags
CtxContextCleanup, //ContextCleanupCallback
CTX_INSTANCE_CONTEXT_SIZE, //Size
CTX_INSTANCE_CONTEXT_TAG }, //PoolTag
{ FLT_FILE_CONTEXT, //ContextType
0, //Flags
CtxContextCleanup, //ContextCleanupCallback
CTX_FILE_CONTEXT_SIZE, //Size
CTX_FILE_CONTEXT_TAG }, //PoolTag
{ FLT_STREAM_CONTEXT, //ContextType
0, //Flags
CtxContextCleanup, //ContextCleanupCallback
CTX_STREAM_CONTEXT_SIZE, //Size
CTX_STREAM_CONTEXT_TAG }, //PoolTag
{ FLT_STREAMHANDLE_CONTEXT, //ContextType
0, //Flags
CtxContextCleanup, //ContextCleanupCallback
CTX_STREAMHANDLE_CONTEXT_SIZE, //Size
CTX_STREAMHANDLE_CONTEXT_TAG }, //PoolTag
{ FLT_CONTEXT_END }
};

Creating Contexts

Once a minifilter driver has registered the context types that it uses, it can create a context by calling FltAllocateContext. This routine selects the appropriate context definition to use according to the criteria described in Registering Context Types.

In the following code example, taken from the CTX sample minifilter driver, the CtxInstanceSetup routine calls FltAllocateContext to create an instance context:

status = FltAllocateContext(
FltObjects->Filter, //Filter
FLT_INSTANCE_CONTEXT, //ContextType
CTX_INSTANCE_CONTEXT_SIZE, //ContextSize
NonPagedPool, //PoolType
&instanceContext); //ReturnedContext

In the CTX sample, the following context definition is registered for instance contexts:

{ FLT_INSTANCE_CONTEXT, //ContextType
0, //Flags
CtxContextCleanup, //ContextCleanupCallback
CTX_INSTANCE_CONTEXT_SIZE, //Size
CTX_INSTANCE_CONTEXT_TAG }, //PoolTag

This is a fixed-size context definition, because the Size member is a constant. (If the Size member were FLT_VARIABLE_SIZED_CONTEXTS, it would be a variable-size context definition.) Note that the FLTFL_CONTEXT_REGISTRATION_NO_EXACT_SIZE_MATCH flag is not set in the Flags member. In this case, if the value of the Size parameter of FltAllocateContext matches that of the Size member of the context definition, FltAllocateContext allocates the instance context from the appropriate nonpaged lookaside list. If the values do not match, FltAllocateContext fails with a return value of STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND.

FltAllocateContext initializes the reference count on the new context to one. When the context is no longer needed, the minifilter driver must release this reference. Thus, every call to FltAllocateContext must be matched by a subsequent call to FltReleaseContext.

Setting Contexts

After creating a new context, a minifilter driver can attach it to an object by calling FltSetXxxContext, where Xxx is the context type.

If the Operation parameter of the FltSetXxxContext routine is set to FLT_SET_CONTEXT_KEEP_IF_EXISTS, FltSetXxxContext attaches the newly allocated context to the object only if the minifilter driver has not already set a context for the object. If the minifilter driver has already set a context, FltSetXxxContext returns STATUS_FLT_CONTEXT_ALREADY_DEFINED, which is an NTSTATUS error code, and does not replace the existing context. If the OldContext parameter of the FltSetXxxContext routine is non-NULL, it receives a pointer to the existing context. When this pointer is no longer needed, the minifilter driver must release it by calling FltReleaseContext.

If the Operation parameter is set to FLT_SET_CONTEXT_REPLACE_IF_EXISTS, FltSetXxxContext always attaches the new context to the object. If the minifilter driver has already set a context, FltSetXxxContext deletes the existing context, sets the new context, and increments the reference count on the new context. If the OldContext parameter is non-NULL, it receives a pointer to the deleted context. When this pointer is no longer needed, the minifilter driver must release it by calling FltReleaseContext.

In the following code example, taken from the CTX sample minifilter driver, the CtxInstanceSetup routine creates and sets an instance context:

status = FltAllocateContext(
FltObjects->Filter, //Filter
FLT_INSTANCE_CONTEXT, //ContextType
CTX_INSTANCE_CONTEXT_SIZE, //ContextSize
NonPagedPool, //PoolType
&instanceContext); //ReturnedContext
...
status = FltSetInstanceContext(
FltObjects->Instance, //Instance
FLT_SET_CONTEXT_KEEP_IF_EXISTS, //Operation
instanceContext, //NewContext
NULL); //OldContext

if (instanceContext != NULL) {
FltReleaseContext(instanceContext);
}
return status;


Note that after the call to FltSetInstanceContext, there is a call to FltReleaseContext to release the reference count that was set by FltAllocateContext (not FltSetInstanceContext). This is explained in Releasing Contexts.

Getting Contexts

Once a minifilter driver has set a context for an object, it can get the context by calling FltGetXxxContext, where Xxx is the context type.

In the following code example, taken from the SwapBuffers sample minifilter driver, the minifilter driver calls FltGetVolumeContext to get a volume context:

status = FltGetVolumeContext(
FltObjects->Filter, //Filter
FltObjects->Volume, //Volume
&volCtx); //Context
...
if (volCtx != NULL) {
FltReleaseContext(volCtx);
}

If the call to FltGetVolumeContext is successful, the Context parameter receives the address of the caller's volume context. FltGetVolumeContext increments the reference count on the Context pointer. Thus, when this pointer is no longer needed, the minifilter driver must release it by calling FltReleaseContext.

Referencing Contexts

The filter manager uses reference counting to manage the lifetime of a minifilter driver context. A reference count is a number indicating the state of a context. Whenever a context is created or is referenced by a system component, the reference count of the context is incremented by one. When a context is no longer needed, its reference count is decremented. A positive reference count means that the context is usable. When the reference count becomes zero, the context is unusable, and the filter manager eventually frees it.

A minifilter driver can add its own reference to a context by calling FltReferenceContext to increment the context's reference count. This added reference must eventually be removed by calling FltReleaseContext.

Releasing Contexts

A minifilter driver releases a context by calling FltReleaseContext. Every successful call to one of the following routines must eventually be matched by a call to FltReleaseContext:

FltAllocateContext

FltGetXxxContext

FltReferenceContext

Note that the OldContext pointer returned by FltSetXxxContext and the Context pointer returned by FltDeleteContext must also be released when they are no longer needed.

In the following code example, taken from the CTX sample minifilter driver, the CtxInstanceSetup routine creates and sets an instance context and then calls FltReleaseContext:

status = FltAllocateContext(
FltObjects->Filter, //Filter
FLT_INSTANCE_CONTEXT, //ContextType
CTX_INSTANCE_CONTEXT_SIZE, //ContextSize
NonPagedPool, //PoolType
&instanceContext); //ReturnedContext
...
status = FltSetInstanceContext(
FltObjects->Instance, //Instance
FLT_SET_CONTEXT_KEEP_IF_EXISTS, //Operation
instanceContext, //NewContext
NULL); //OldContext

if (instanceContext != NULL) {
FltReleaseContext(instanceContext);
}
return status;


Note that FltReleaseContext is called regardless of whether the call to FltSetInstanceContext succeeds. In both cases, the caller must call FltReleaseContext to release the reference set by FltAllocateContext (not FltSetInstanceContext).

If the context is successfully set for the instance, FltSetInstanceContext adds its own reference to the instance context. Thus, the reference set by FltAllocateContext is no longer needed, and the call to FltReleaseContext removes it.

If the call to FltSetInstanceContext fails, the instance context has only one reference, namely the one set by FltAllocateContext. When FltReleaseContext returns, the instance context has a reference count of zero, and it is freed by the filter manager.

Deleting Contexts

Every context that is set by a successful call to FltSetXxxContext must eventually be deleted. However, the filter manager deletes contexts automatically when the objects that they are attached to are deleted, when a minifilter driver instance is detached from a volume, or when the minifilter driver is unloaded. Thus, it is rarely necessary for a minifilter driver to explicitly delete a context.

A minifilter driver can delete a context by calling FltDeleteXxxContext, where Xxx is the context type, or by calling FltDeleteContext.

A context can be deleted only if it is currently set for an object. A context cannot be deleted if it has not yet been set, or if it has already been replaced by a successful call to FltSetXxxContext.

In the call to FltDeleteXxxContext, the old context is returned in the OldContext parameter, if it is non-NULL. If the OldContext parameter is NULL, the filter manager decrements the reference count on the context, which is then freed unless the minifilter driver has an outstanding reference on it.

The following code example shows how to delete a stream context:

status = FltDeleteStreamContext(
FltObjects->Instance, //Instance
FltObjects->FileObject, //FileObject
&oldContext); //OldContext
...
if (oldContext != NULL) {
FltReleaseContext(oldContext);
}

In this example, FltDeleteStreamContext removes the stream context from the stream, but it does not decrement the context's reference count, because the OldContext parameter is non-NULL. FltDeleteStreamContext returns the address of the deleted context in the OldContext parameter. After performing any needed processing, the caller must release the deleted context by calling FltReleaseContext.

Freeing Contexts

A context is freed after it is deleted and all outstanding references to it have been released.

There is one exception to this rule: if a context has been created but has not been set by calling FltSetXxxContext, it does not need to be deleted. It is freed when its reference count reaches zero. (See the code example in Releasing Contexts.)

When a minifilter driver registers its context types, each context definition can optionally include a context cleanup callback routine to be called before the context is freed. For more information, see PFLT_CONTEXT_CLEANUP_CALLBACK.

File System Support for Contexts

To support file contexts (if applicable), stream contexts, and file object (stream handle) contexts, a file system must use the FSRTL_ADVANCED_FCB_HEADER structure. All Microsoft Windows file systems use this structure, and all third-party file system developers are strongly encouraged to do so as well. For more information, see FsRtlSetupAdvancedHeader and FSRTL_ADVANCED_FCB_HEADER.

The NTFS and FAT file systems do not support file, stream, or file object contexts on paging files, in the pre-create or post-close path, or for IRP_MJ_NETWORK_QUERY_OPEN operations.

A minifilter driver can determine whether a file system supports stream contexts and file object contexts for a given file object by calling FltSupportsStreamContexts and FltSupportsStreamHandleContexts, respectively.

File contexts are available on Windows Vista and later.

For file systems (such as FAT) that support only a single data stream per file, file contexts are equivalent to stream contexts. Such file systems usually support stream contexts but do not support file contexts. Instead, the filter manager provides this support, using the file system's existing support for stream contexts. For minifilter driver instances attached to these file systems, FltSupportsFileContexts returns FALSE, while FltSupportsFileContextsEx returns TRUE (when a valid non-NULL value is passed for the Instance parameter).

To support file contexts, a file system must:

·         Embed a FileContextSupportPointer member of type PVOID in its file context structure, usually the file context block (FCB). The file system must initialize this member to NULL.

·         Use FsRtlSetupAdvancedHeaderEx (instead of FsRtlSetupAdvancedHeader) to initialize its stream context structure, passing a valid pointer to the FileContextSupportPointer member (embedded in the corresponding file context structure) for the FileContextSupportPointer parameter. For more information, see FsRtlSetupAdvancedHeaderEx and FSRTL_ADVANCED_FCB_HEADER.

·         Call FsRtlTeardownPerFileContexts to free all file context structures that filter and minifilter drivers have associated with a file when the file system deletes its file context structure for the file.