Thursday, February 11, 2010

Filter Manager Concepts: Part 5 – CONTEXT_NODE

Why does one need contexts ? Well, the IO model in NT is based on passing objects around and the various components that handle these objects need a way to save information about each object (for example the file system might need to ‘remember’ where the file is located on disk). This information is basically “context” for that object. Naturally, there might be multiple components that process a certain object and so each object has multiple context associated with it.

Support for contexts in NT in general appears to have been done in a pretty ad hoc manner. For example, some components in the system use the object itself to store their context (for example the IrpList in the FILE_OBJECT). Some other components use special fields in the object (the FsContext in the FILE_OBJECT is where a file system can put a pointer to its context, known as the FCB or SCB). Some objects have an “extension” where whoever allocates it can store their context (like DEVICE_OBJECT and the extension). And finally some objects allow storing and retrieving any structure (FSRTL_ADVANCED_FCB_HEADER and FsRtlInsertPerStreamContext). As you can see this is quite messy and filter manager’s abstractions make things somewhat more consistent for minifilters. These abstractions are pretty well documented here. There is also a sample in the WDK, “ctx” which shows how to call the APIs and all that.

From looking at the APIs one can see that the user contexts are declared as PFLT_CONTEXT. However, this is not a structure at all, it’s simply an alias to PVOID. The real structure that stores the information about the context is CONTEXT_NODE:

0: kd> !fltkd.ctx fffff980075dac30
CONTEXT_NODE: fffff980075dac30  [0002] InstanceContext NonPagedPool
   ALLOCATE_CONTEXT_NODE: fffff98007a8cd80 "luafv" [01] LookasideList (size=968)
   AttachedObject           : fffff98007aa64c0
   UseCount                 : 1
   TREE_NODE: fffff980075dac60 (k1=fefefefefefefefe, k2=fefefefefefefefe) [00010000] InTree
   UserData                 : fffff980075daca0
   UserDataSize             : 856
   FilterLink               : [fffff98007b9e9b8-fffff98007b9e9b8]

UseCount is the number of references for the context. If it gets zero then the context will be freed. All contexts have an initial reference, which is the reference from the underlying structure (FLT_INSTANCE in this case) so UseCount is usually 1. When the object goes away this reference is released and the context goes away as well (if there aren’t any other references). This reference represents the link from the object to the context (FLT_INSTANCE for example has a member “Context” that points to the context, while other structures have a CONTEXT_LIST_CTRL structure to which contexts are attached).

I sometimes get asked about the difference between FltDeleteContext and FltReleaseContext. FltDeleteContext releases the “initial” reference, from the underlying object (and also removes the link from that structure to the context so from that point on any function to Get the context will not find it). The caller of FltDeleteContext must have a valid reference (since it takes a PFLT_CONTEXT parameter) so the reference count before calling FltDeleteContext is at least 2. After FltDeleteContext releases the reference from the underlying object the context is still valid and the caller still has a reference to it (but it’s not linked to the object anymore). Once the caller calls FltReleaseContext the context might go away (and it will go away if it was the last reference). So if someone wants to delete a context they must call FltDeleteContext and then FltReleaseContext, in this order.

Another thing to note is that the context is linked into two different structures: the underlying structure that it is attached to (usually through TREE_NODE) and the filter that allocated it (through the FilterLink member).