Thursday, February 11, 2010

Verifier Checks: A filter has completed an operation but does not implement name provider callbacks

FILTER VERIFIER ERROR:   A filter has completed an operation but does not implement name provider callbacks. For example if a filter completes an IRP_MJ_CREATE and is not a name provider it is possible that during name queries the FO will make it's way to the file system and bugcheck. (Filter = Xxx, Cbd = Xxx)

This is a new check in Win7 and I get a lot of questions about it. In order to understand what it means we should talk briefly about how FltGetFileNameInformation and friends work.

In order to normalize a file name Filter Manager will get a path to a file in a number of different ways, including by querying the file system for it, and then it will open directories on that path and query for long names. In order to optimize this as much as possible (as you can imagine this is rather time consuming… incidentally, a minifilter should use opened names where possible as the perf is better) the decision was made to implement a separate mechanism from the normal IO path. Minifilters that change the path in any way should implement some additional callbacks, which we refer to as “name provider callbacks” and filter manager will call those callbacks when it needs to generate and normalize a name. If a minifilter does implement those then it is called a name provider. The best part about this is that if a minifilter is not a name provider then it can stay out of the name resolution path completely. If none of the minifilters on a volume are name providers then filter manager can skip all of them when it tries to resolve a name and go directly to the driver below.

How does this tie into the verifier check above ? If filter manager sees a filter successfully complete any operation that might have an impact on the namespace (like a IRP_MJ_SET_INFORMATION with FileRenameInformation or FileLinkInformation or FileShortNameInformation and so on) then it expects that the minifilter implements the name provider callbacks as well. Now let’s say that the minifilter successfully completed a rename for file “A” to file “B”. If the minifilter in question does not implement name provider callbacks, filter manager will not even ask it for the name. It will go straight to the name provider below (or the file system) and it will get back the old name , “A”. This clearly breaks the abstraction that each filter must implement.

There is one more operation other than those that change names that is particularly important. If a minifilter successfully completes IRP_MJ_CREATE (in this particular case returning STATUS_REPARSE does not count as success) then the FILE_OBJECT should NEVER under any circumstances be seen below that minifilter (the file system will try to interpret the private fields in it as its own, which can lead to bugchecks or data corruption). So if a minifilter successfully completes a create but doesn’t implement name provider callbacks then name queries will bypass it completely and the FILE_OBJECT will be shown below.

So now that we explained all the context, let’s go into what’s allowed and what’s not:

  • a minifilter can always fail any operation, including IRP_MJ_CREATE and IRP_MJ_SET_INFORMATION without implementing name provider callbacks. Also, completing an IRP_MJ_CREATE with STATUS_REPARSE counts as a failure in this context, even though STATUS_REPARSE is a success code.
  • if a minifilter successfully completes an IRP_MJ_CREATE (excluding STATUS_REPARSE), even if it doesn’t change the name space at all (like an SFO (shadow file object) type of filter) it must still implement name provider callbacks, even if they do nothing and are only passthrough (meaning that they get the name from below by calling FltGetFileNameInformation and return it). This minifilter is called the owner of the FILE_OBJECT.
  • if a minifilter implements any type of name space virtualization (which means that if there is any difference at all between the namespace above the minifilter and the namespace below the minifilter) then it must implement name provider callbacks and it must also implement (or at least make a conscious decision not to support some of the features) all the other name support operations, like directory enumeration, setting and querying short names, directory change notification, dile IDs, renames, hardlinks, reparse points and so on. This is true even if the minifilter does not own any file object (i.e. it never completes an IRP_MJ_CREATE but rather it just changes where files are on the file system).
  • a minifilter should NEVER skip any portion of the IO stack because there is no way to know if the FILE_OBJECT it cares about belongs above or below the filter. For example, a minifilter should never take a FILE_OBJECT and do something like allocate an IRP and send it directly to the file system below, since it might bypass the owner of that FILE_OBJECT. A far more common scenario of this same behavior is a minifilter that receives a HANDLE from its user mode component and it calls ObReferenceObjectByHandle and then it call some FltXxx function with that FILE_OBJECT, without realizing that the owner of that FILE_OBJECT might be above itself.

I hope this makes sense. As always, if you have any questions please don’t hesitate to ask.