So in order to discuss the interesting aspects of IRP_MJ_NETWORK_QUERY_OPEN we need to discuss when FastIoQueryOpen is called and why. The main reason for this FastIo is that it wraps a set of operations (IRP_MJ_CREATE, IRP_MJ_QUERY_INFORMATION(FILE_NETWORK_OPEN_INFORMATION), IRP_MJ_CLEANUP,IRP_MJ_CLOSE) into one call, allowing the caller to get information about a file or just to check whether the file exists directly by name, without actually opening the file (see http://blogs.msdn.com/b/oldnewthing/archive/2007/10/23/5612082.aspx). This might not look like much but when dealing with a network protocol being able to do multiple things in one request can improve performance a lot. So it seems that this particular FastIo was introduced to really optimize this one thing, getting the FILE_NETWORK_OPEN_INFORMATION for a file by name.
In kernel mode one way to generate this FastIo (but not the only way) is by calling IoFastQueryNetworkAttributes(). This function is not documented in MSDN but it's pretty straightforward to figure out (though minifilters should not call this API since it breaks layering… It might be useful for testing IRP_MJ_NETWORK_QUERY_OPEN so that's way I'm mentioning it here). The way it works is by eventually calling IopParseDevice with an OPEN_PACKET that has the QueryOnly flag set. What happens in this case is that IopParseDevice allocates an IRP_MJ_CREATE IRP and passes it as a parameter to the FastIoQueryOpen callback. If the call is successful then IopParseDevice returns, if it is not then the already allocated IRP_MJ_CREATE IRP is sent down the stack the usual way.
One thing that is very interesting is the way the callbacks are defined:
This snippet is taken from the MSDN page: FLT_PARAMETERS for IRP_MJ_NETWORK_QUERY_OPEN Union. There are quite a few things mentioned on that page that are interesting:typedef union _FLT_PARAMETERS { ... ; struct { PIRP Irp; PFILE_NETWORK_OPEN_INFORMATION NetworkInformation; } NetworkQueryOpen; ... ; } FLT_PARAMETERS, *PFLT_PARAMETERS;
- First and foremost there is the IRP parameter. This is interesting because it's the only place in a minifilter where one gets to see an IRP. I remember hearing that there was some debate in the FltMgr team whether this IRP should be wrapped in a FLT_CALLBACK_DATA (or maybe just an FLT_IO_PARAMETER_BLOCK), but it wouldn't be pretty no matter what and having an IRP here doesn't really change anything as it should be treated just like a structure (as opposed to a regular IRP on which methods like IoCallDriver can be called).
- Then there is the note that "The file object associated with IRP_MJ_NETWORK_QUERY_OPEN is a stack-based object.A filter registered for the NetworkQueryOpen callback must not reference this object. That is, do not call ObReferenceObject or ObDereferenceObject on this stack-based file object. Also, do not save a pointer to the object.". The main idea here is that this particular operation was designed to be fast and obviously just allocating the FILE_OBJECT structure on the stack and then initializing some members in it is faster than allocating a full FILE_OBJECT. Also, the FILE_OBJECT is not going to be used anywhere, it is simply a way to let the file system know the path to the file for which the FILE_NETWORK_OPEN_INFORMATION is required. If the FastIo doesn’t work then when the full IRP_MJ_CREATE is sent down a full FILE_OBJECT is allocated.
- Also there is a mention that "A filter must register for this operation". This is not true. A minifilter can safely ignore this operation.
- When the IRP_MJ_NETWORK_QUERY_OPEN callback is called, the FILE_OBJECT is not opened and the minifilter is technically in a preCreate state. So trying to use any FILE_OBJECT related context (like FileContext, StreamContext and StreamHandleContext) will not work.
- Moreover, the FILE_OBJECT is not a real FILE_OBJECT so there are some things that don't really apply to it (maybe not all the flags or all the members are set like they would on a real FILE_OBJECT - I didn't actually ever check; at least reference counting seems to not work as the note indicates).
- When completing IRP_MJ_NETWORK_QUERY_OPEN with something other than STATUS_FLT_DISALLOW_FAST_IO (such as when the minifilter actually completes the operation successfully), the minifilter must set the status into IRP->IoStatus and not into the FLT_CALLBACK_DATA. The FLT_CALLBACK_DATA represents the current operation and the status is the status for the FastIo and not the status for the IRP. For example a minifilter that wants to complete IRP_MJ_NETWORK_QUERY_OPEN call to indicate that the file doesn't exist should set Irp->IoStatus.Status to the appropriate status (like STATUS_OBJECT_NAME_NOT_FOUND or STATUS_OBJECT_PATH_NOT_FOUND or whatever) and the CallbackData->IoStatus.Status to STATUS_SUCCESS to indicate that the FastIo has completed successfully.
- Finally there are some layering issues with IRP_MJ_NETWORK_QUERY_OPEN which make it possible for minifilters to have their callback invoked for operations that should be layered below them. These are rather rare cases but they can happen and are painful to fix and to debug.
No comments:
Post a Comment