Thursday, July 26, 2012

Testing Case-Sensitive Behavior

In this post I want to talk about case sensitivity and how to test such behavior for file systems and file system filters. I've addressed various case-sensitive file system filter specific topics in my previous posts on FILE_OBJECT Names in IRP_MJ_CREATE and on Names in Minifilters - Implementing Name Provider Callbacks.
This isn't a particular complicated subject but a developer must be careful in their implementation to support this concept. Other than figuring out the case sensitivity of the name in the IRP_MJ_CREATE path and in the name provider paths, a developer must in general pay attention to their implementation so that if, for example, they compare names or file extensions and they would use RtlEqualUnicodeString or RtlCompareUnicodeString then they must use the appropriate value for the CaseInSensitive parameter, deduced from the IRP_MJ_CREATE parameters or from the FILE_OBJECT flags. Also, when dealing with UNICODE_STRINGs, one should never try to change the case by directly touching the bits in the string, and instead always use functions like RtlUpcaseUnicodeString and RtlUpcaseUnicodeChar (and their counterparts, RtlDowncaseUnicodeString and RtlDowncaseUnicodeChar).
However, for this post I don't want to focus on implementation but on how to test this. In my opinion the best place to start is the IFS Test suite, since it has a lot of tests that deal with the create path, and in particular some that test both case sensitive behavior and case preserving behavior. However, when running this test on a regular system it will most likely fail with this message:
E94.E98 :  +TEST+SEV2      : Test         :CaseSensitiveTest
Group        :OpenCreateGeneral
Status       :C000001E (IFSTEST_TEST_NTAPI_FAILURE_CODE)
LastNtStatus     :C0000035 STATUS_OBJECT_NAME_COLLISION
ExpectedNtStatus :00000000 STATUS_SUCCESS
Description  :{Msg# OpCreatG!casesen!14} Failure while creating a
              test file \casesen\NeSesac.dat. Note that in a previous
              step we created another test file. That other test
              file \casesen\nesesac.dat, differs from this one by
              the case of the name. 
Lookup Query : http://www.microsoft.com/ContentRedirect.asp?prd=IFSKit&id=OpCreatG!casesen!14&pver=2195
This happens because by default Windows is case insensitive and so naturally the case sensitivity test fails. In order to enable case sensitive behavior for Windows all you need to do is change this registry value (it should be 1 be default so change it to 0):
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\kernel\obcaseinsensitive
BTW, this is described in Microsoft's KB 817921, even though the case deals with something else entirely.
So anyway, once you set this key make sure that the file system itself is case sensitive and then test your filter like you normally would, though it might be a good idea to add a couple of tests with files that have the same name with different cases, just to make sure you're exercising the relevant code paths. For example, if implementing an anti-virus program it might be interesting to test with a clean file with the name "foo.txt" and the EICAR file "Foo.txt" in the same folder, to make sure that not only the scanning happens on the apropriate file, but also that the other parts of the program (such as the quarantine engine or the cleaning engine) work as expected. Another good test is to then switch file names (so that "Foo.txt" is clean and "foo.txt" is the EICAR file), so that in case the product somehow depends on the sorting order in the directory that issue can be found and addressed.

Finally, this is an easy way to check that the file system is case sensitive (UPDATE: as Phil pointed out in the comments, this doesn't mean that the requests are going to be case sensitive, it simply means the file system supports it; whether this is on or off generally depends on the file system implementation and not the registry key value):

C:\Windows\system32>fsutil fsinfo volumeinfo C:
Volume Name : System
Volume Serial Number : 0x4297004a
Max Component Length : 255
File System Name : NTFS
Supports Case-sensitive filenames
Preserves Case of filenames
Supports Unicode in filenames
Preserves & Enforces ACL's
Supports file-based Compression
Supports Disk Quotas
Supports Sparse files
Supports Reparse Points
Supports Object Identifiers
Supports Encrypted File System
Supports Named Streams
Supports Transactions
Supports Hard Links
Supports Extended Attributes
Supports Open By FileID
Supports USN Journal

Thursday, July 19, 2012

Detaching Instances and FLTFL_POST_OPERATION_DRAINING

I've recently realized that FLTFL_POST_OPERATION_DRAINING is not a very well understood feature. This is even though it is pretty well documented on the page for the PFLT_POST_OPERATION_CALLBACK function. So I think that in addition to the documentation page it might help to take a look at what happens under the hood.

So once a minifilter instance attaches to a volume it will start receiving IRPs and the filter will process them. There are many cases where filters need to process the results of the operation after the file system has completed it so the filter would register PostOperation callbacks and the filter would return an appropriate status to indicate that it wants a that PostOp callback called for each specific operation. These IRPs are generally called in-flight requests and in many cases the filter has data allocated associated with the operation (buffers allocated and possibly even replaced on the request, contexts that are sent from the PreOp callback to the PostOp callback, references (if the filter references a context in PreOp and dereferences it in PostOp) and so on). In case the instance needs to be detached (the minifilter is unloaded or just one instance is detached from the underlying volume) from a live volume, IO will continue to flow on the volume. One way to detach is to stop receiving new IO requests and then just wait for all the in-flight operations to be completed by the file system. Once all the in-flight operations have completed then FltMgr can pretty much free the instance because there can not be any more IO on it.

Unfortunately, this approach has a pretty big disadvantage. It is impossible to predict when the file system will complete all in-flight IRPs. There are certain operations that are very long-lived (for example, the directory change notification IRPs can be held in the file system indefinitely) which would make the time it takes to unload a filter or to detach an instance completely unpredictable. FltMgr howver is smarter than that and what it actually does is that it always tracks for each instance which operations are in-flight at any given time. So when an instance needs to be detached or a filter unloaded, FltMgr can actually know that it doesn't need to call any PostOp callbacks for that filter or that instance. However, there is still data associated with the request, buffers that have been replaced, contexts that are allocated and so on. If FltMgr simply stops calling the PostOp callbacks for all in-flight operations then the allocated data and the buffers would never be freed and the references would be leaked.

This is where FLTFL_POST_OPERATION_DRAINING comes in. When an instance is detached, FltMgr disconnects the instances from the IO path and then calls each PostOp callback for all the in-flight operations with the FLTFL_POST_OPERATION_DRAINING flag set. When a PostOp callback is called with FLTFL_POST_OPERATION_DRAINING flag the callback must not do any actual work (like checking the status of the operation or touching the FLT_CALLBACK_DATA structure at all) and instead the filter must just release any buffers or references they might have allocated. For most filters this is generally similar to what they do when the operation is unsuccessful (though there might be cases where this is different so this is more of a hint than an actual guideline to implement this feature). It is important to note that when the PostOp callback is called with the FLTFL_POST_OPERATION_DRAINING flag the actual operation might have already been completed or it might still be in the file system somewhere so the filter must not make any assumptions about the status of the actual operation.

Thursday, July 12, 2012

The Flags of FILE_OBJECTs - Part V

I wasn't really planning on adding another part to the series but there were still a couple of things I wanted to mention.
First, as I'm sure you've noticed if you looked at the actual flag values in the wdm.h file, there seems to be a flag missing, with the value 0x00800000, which would put it between FO_VOLUME_OPEN and FO_REMOTE_ORIGIN. Well, the easiest way to solve this mystery is to look at an older DDK where you'll find the missing flag:
  • FO_FILE_OBJECT_HAS_EXTENSION - 0x00800000
So what is this FO_FILE_OBJECT_HAS_EXTENSION and why is it gone from pretty much everything from documentation to the !fileobj extension (as I mentioned in my previous posts, an easy way to figure out flag values is to set them into a structure and use one of extension that knows how to parse that structure to see what name it displays; in this case however the !fileobj extension ignored that flag). As far as I can tell, this flag is no longer used since Vista, but before Vista this was used to indicate that there was some private data allocated past the end of the FILE_OBJECT (see this thread on NTFSD). From the thread it appears that the IO manager used this flag to store the targeting information associated with the FILE_OBJECT, which was also used by FltMgr (see this other thread on NTFSD as well). Since Vista this functionality is implemented using the FSRTL_PER_FILEOBJECT_CONTEXT structure. I've explored the way this works in my previous post on tracking a minifilter's ActiveOpens files.
Anyway, this FILE_OBJECT extension seems to have been used to implement the same functionality that FsRtlInsertPerFileObjectContext provides today. FltMgr's designers had a goal to make sure that everything that FltMgr does can be implemented by a 3rd party filter and so since they couldn't expect 3rd party filters to modify private OS structures they had exposed the same functionality through the FSRTL_PER_FILEOBJECT_CONTEXT structure and related functions (FsRtlInitPerFileObjectContext, FsRtlInsertPerFileObjectContext, FsRtlLookupPerFileObjectContext, FsRtlRemovePerFileObjectContext). Since this is implemented in a different way today it makes sense that this flag is obsolete for now. Please do note, however, that it's entirely possible that it will be reused at some point in the future.
Another thing I wanted to do was to take a closer look at how the FO_DISALLOW_EXCLUSIVE flag can be used, since as I mentioned in my previous post in the series I wasn't sure what it does. Well, after some investigation I have some idea what it does but I'm not sure why exactly this behavior would be desired. So first I'd like to mention that this flag doesn't seem to be used anywhere in the WDK, neither FastFat nor CDFS seem to need it. I couldn't find anything about it online. Naturally I searched for both FO_DISALLOW_EXCLUSIVE and FILE_DISALLOW_EXCLUSIVE but there was really no mention about what they might do or where they might be used. However, there was one bit of information in the WDK that helped a lot: the fact that FO_DISALLOW_EXCLUSIVE is the only flag defined for FO_FLAGS_VALID_ONLY_DURING_CREATE. So I decided to take a very close look at Ntfs' IRP_MJ_CREATE processing in the hope that I might be able to figure out where this flag might be used.
So after some debugging and using my favorite disassembler, IDA, I've been able to find a couple of places where this flag might be checked. In particular the Ntfs!NtfsOpenAttribute function looked very promising since it had code that checked for that exact value, and moreover it was using the same offset into a structure that the FILE_OBJECT->Flags are at:
1: kd> dt nt!_FILE_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
...
   +0x02c Flags            : Uint4B
...
   +0x07c FileObjectExtension : Ptr32 Void

1: kd> u 960e7c14 L1
Ntfs!NtfsOpenAttribute+0x1f0:
960e7c14 f7402c00000002  test    dword ptr [eax+2Ch],2000000h

1: kd> u 960e7ded L1
Ntfs!NtfsOpenAttribute+0x3ca:
960e7ded f7402c00000002  test    dword ptr [eax+2Ch],2000000h

So I figured I had a pretty good shot at finding out how this is used by looking at when these flags are checked (the path that leads to the checks). That involved a lot of debugging and backtracking, good thing VMWare makes it so easy to retry exactly the same code path over and over again :). Anyway, this is the piece of code that turned out to be relevant:
1: kd> u Ntfs!NtfsOpenAttribute+0x3af L0x10
Ntfs!NtfsOpenAttribute+0x3b0:
960e7dd3 7328            jae     Ntfs!NtfsOpenAttribute+0x3da (960e7dfd)
960e7dd5 ff7518          push    dword ptr [ebp+18h]
960e7dd8 50              push    eax
960e7dd9 53              push    ebx
960e7dda e8e9d70100      call    Ntfs!NtfsWriteCheck (961055c8)
960e7ddf 8845e7          mov     byte ptr [ebp-19h],al
960e7de2 c645e601        mov     byte ptr [ebp-1Ah],1
960e7de6 84c0            test    al,al
960e7de8 7535            jne     Ntfs!NtfsOpenAttribute+0x3fc (960e7e1f)
960e7dea 8b4618          mov     eax,dword ptr [esi+18h]
960e7ded f7402c00000002  test    dword ptr [eax+2Ch],2000000h
960e7df4 7429            je      Ntfs!NtfsOpenAttribute+0x3fc (960e7e1f)
960e7df6 a0187c0996      mov     al,byte ptr [Ntfs!NtfsStatusDebugEnabled (96097c18)]
960e7dfb 84c0            test    al,al
960e7dfd 7414            je      Ntfs!NtfsOpenAttribute+0x3f0 (960e7e13)
960e7dff 681f3a0000      push    3A1Fh

1: kd> u 960e7e13
Ntfs!NtfsOpenAttribute+0x3f0:
960e7e13 c745e0220000c0  mov     dword ptr [ebp-20h],0C0000022h
960e7e1a e98f040000      jmp     Ntfs!NtfsOpenAttribute+0x88a (960e82ae)
What this piece of code checks is that if the file is not writable and this flag is set and the user wants to open the file exclusively (doesn't share READ, WRITE and DELETE; this particular check happens a bit further up in the code, not pictured here…) then the IRP_MJ_CREATE will fail with error 0xC0000022, STATUS_ACCESS_DENIED. I've been able to validate that this is the case using FileTest (during debugging I resorted to nasty tricks to get the thread to take that path I wanted, so I had to validate that this can be done by regular creates too), by creating a file with a Deny Write DACL for EVERYONE (so that Ntfs!NtfsWriteCheck would fail) and then opening it exclusively. It works fine if I don't specify FILE_DISALLOW_EXCLUSIVE but fails with STATUS_ACCESS_DENIED when I add FILE_DISALLOW_EXCLUSIVE to the options.
As I said before I'm not sure why this is useful, so if you know a scenario where this might be useful please add a comment and enlighten me :). Anyway, this is what I've been to figure out about this flag so I hope it helps...

Thursday, June 28, 2012

The Flags of FILE_OBJECTs - Part IV

This is the last part in the series of posts on the flags of the FILE_OBJECT. The previous posts can be found here:

  1. The Flags of FILE_OBJECTs - Part I
  2. The Flags of FILE_OBJECTs - Part II
  3. The Flags of FILE_OBJECTs - Part III

And here is the final set of flags:

  • FO_FILE_FAST_IO_READ - 0x00080000 - according to the documentation page for the FILE_OBJECT structure, this flag is set to indicate that a fast I/O read was performed on this FILE_OBJECT. However, looking at the FastFat source code reveals more. First, the flag can be set when the file is read through a regular request as well, not only through fast IO (see FatCommonRead and FatMultiAsyncCompletionRoutine). There is also an interesting bit in the create path (FatCommonCreate), where the flag is set if the caller requested EXECUTE access. So it appears that in fact this flag is set when the user performs a read on the file, but not for paging reads (which makes sense, paging reads are, in a way, the system reading from a file and not exactly the user). The reason for this becomes clear when looking at where the flag is used in FastFat: in the FatUpdateDirentFromFcb function, which uses the flag to figure out if it needs to update the last access time on the file.
  • FO_RANDOM_ACCESS - 0x00100000 - this flag is set to indicate that the caller intends to use this FILE_OBJECT for random access (forward and backward seeks). It maps to the FILE_RANDOM_ACCESS flag and is set when creating the handle. The flag is pretty similar to the FO_SEQUENTIAL_ONLY but works in the opposite way: it's an indication to the cache manager that it can't assume that once a location in the file was touched once it won't be touched again so it shouldn't be overly aggressive in removing things from the cache. Unlike FO_SEQUENTIAL_ONLY this flag can't be set or queried using the FileModeInformation.
  • FO_FILE_OPEN_CANCELLED - 0x00200000 - this flag indicates that the IRP_MJ_CREATE for the FILE_OBJECT was cancelled. In other words, someone called IoCancelFileOpen or FltCancelFileOpen. The flag is set in IoCancelFileOpen and is used in IopParseDevice where the IO manager needs to figure out if it should cleanup the FILE_OBJECT (by issuing an IRP_MJ_CLOSE) or not. The documentation for the FltCancelFileOpen routine is also a pretty good source of information for this flag.
  • FO_VOLUME_OPEN - 0x00400000 - this flag indicates that the FILE_OBJECT is an open for a volume. Interestingly enough this flag is set by the IO manager after the IRP_MJ_CREATE IRP completes, so it would not be available in preCreate. However, preCreate is where it's most useful (see the simrep and MetadataManager minifilter samples in the WDK) and so FltMgr parses the IRP_MJ_CREATE parameters and set the FO_VOLUME_OPEN flag when appropriate so that it's available even in preCreate (see this NTFSD post). This means that this flag is usable by minifilters for all operations. For legacy filters things are more complicated (as usual): they might see the flag set if there is a FltMgr frame above them but they might not see it otherwise (so I guess legacy filters shouldn't expect to see this flag set).
  • FO_REMOTE_ORIGIN - 0x01000000 - this is a flag that indicates that the FILE_OBJECT was created to satisfy a remote request, which is something done by remote file systems. There are two functions that deal with this flag: IoIsFileOriginRemote which pretty much just queries this flag, and IoSetFileOrigin which sets it. The documentation for both functions mentions this connection (between these functions and FO_REMOTE_ORIGIN) but fails to explain why this is done. The reason is that the cache manager handles files coming from a remote file system differently and so this flag is a hint to the cache manager.
  • FO_DISALLOW_EXCLUSIVE - 0x02000000 - this flag is set during the create processing if the FILE_DISALLOW_EXCLUSIVE flag was set on the request. It would appear that this flag can only be set during an IRP_MJ_CREATE operation (there is a definition for FO_FLAGS_VALID_ONLY_DURING_CREATE which is what I'm basing this on). I don't know exactly what this flag does and looking over my notes I've never actually used it or had to interact with it so I guess I'll have to investigate this and write a post on it :).
  • FO_SKIP_COMPLETION_PORT - 0x02000000 - this flag is pretty well explained in the FILE_OBJECT structure page. Additionally, the documentation for SetFileCompletionNotificationModes also explains how this flag might be used (this flag maps to the FILE_SKIP_COMPLETION_PORT_ON_SUCCESS flag). There is also this post that explains a bit more about the flags that affect behavior of IO completion (the following flags): Designing Applications for High Performance - Part III. I'm guessing this flag can be set using the FILE_IO_COMPLETION_NOTIFICATION_INFORMATION structure and respective information class.
  • FO_SKIP_SET_EVENT - 0x04000000 - this flag is similar to FO_SKIP_COMPLETION_PORT. It maps to the FILE_SKIP_SET_EVENT_ON_HANDLE (as per the page for the SetFileCompletionNotificationModes function).
  • FO_SKIP_SET_FAST_IO - 0x08000000 - this flag appears to be similar to the previous ones but doesn’t seem to be something that can be set using SetFileCompletionNotificationModes(). Based on the documentation page for the FILE_OBJECT structure it seems that this flag controls whether the FILE_OBJECT event should be signaled when an operation goes through a fast IO call and therefore completes inline. Naturally in that case there is no need to set the event. Looking at wdm.h it would seem that this flag maps to the FILE_SKIP_SET_USER_EVENT_ON_FAST_IO.

Thursday, June 21, 2012

The Flags of FILE_OBJECTs - Part III

This is the third part in the series about FILE_OBJECT flags. Hopefully the next one will be the last one, I didn't expect this would take this long. Anyway, here is the next set of FILE_OBJECT flags:

  • FO_DIRECT_DEVICE_OPEN - 0x00000800 - this flag indicates that this FILE_OBJECT was the result of a "direct device open". I have already explored this topic in my previous post on Opening Volume Handles in Minifilters. This means that the FILE_OBJECT is in fact a handle to the storage stack volume instead of the file system volume. Please note that this flag is not the same as the FO_VOLUME_OPEN flag (and in fact they're incompatible). This flag isn't really used by file systems at all since the target device is not a file system device but rather the actual storage underneath a file system. As such file system filters will also not be involved in processing FILE_OBJECTs that have this flag set.
  • FO_FILE_MODIFIED - 0x00001000 - this flag indicates that the FILE_OBJECT was used to modify the file. It is primarily used by the file system (to track handles that didn't modify the file or to figure out when it should flush things). As far as I can tell the IO manager doesn't really use it but it will set it. The FastFat sample in the WDK shows in great detail how FO_FILE_MODIFIED is used.
  • FO_FILE_SIZE_CHANGED - 0x00002000 - this flag indicates that the file size was changed using this FILE_OBJECT. This is also primarily used by the file system and the IO manager will only set it. This flag is used in conjunction with FO_FILE_MODIFIED and, like FO_FILE_MODIFIED, the FastFat sample is a great example of a file system uses it.
  • FO_CLEANUP_COMPLETE - 0x00004000 - this flag means that the file system has already processed an IRP_MJ_CLEANUP request on this FILE_OBJECT. There are certain restrictions on which operations are allowed on such FILE_OBJECTs, as can be seen in both the CDFS and FastFat samples. Windows actually uses such FILE_OBJECTs (that have been cleaned up) fairly frequently and thus filters might need to pay attention to this flag in order to be able to figure out which operations might be allowed on it.
  • FO_TEMPORARY_FILE - 0x00008000 - this flag indicates that the file was opened with the FILE_ATTRIBUTE_TEMPORARY. File systems don't seem to do much beyond setting the flag. The consumer of this information is the Cache Manager, that does more aggressive write caching for temporary files (since it's expected that the file will go away anyway and so there is no point in flushing data to disk; please note that memory pressure might require that data gets flushed, it's just that the caching is more aggressive). See the notes under "Caching Behavior" on the page for the CreateFile function.
  • FO_DELETE_ON_CLOSE - 0x00010000 - as noted in my previous post on Using FileModeInformation, I've never seen this flag used anywhere until I looked at the implementation of IopGetModeInformation. So as far as I can tell this flag really isn't used. In the current implementation however it can be read by a query for the FileModeInformation class.
  • FO_OPENED_CASE_SENSITIVE - 0x00020000 - this flag indicates whether name operations on the FILE_OBJECT should be performed in a case-sensitive way or not. This is very important for file system filters that deal with the namespace. This applies not only to filters that change the name space (name providers must know whether to process things in a case-sensitive or in a case-insensitive way) but also for filters that just look at file names and don't actually change anything. There is a related flag that is very important, the SL_CASE_SENSITIVE. I've written multiple posts that mention case sensitivity but the more interesting ones are the one on FILE_OBJECT Names in IRP_MJ_CREATE and the one on Names in Minifilters - Implementing Name Provider Callbacks.
  • FO_HANDLE_CREATED - 0x00040000 - this flag indicates that a handle has been created for the FILE_OBJECT. From the IO manager's perspective the CREATE operation has completed successfully (so it's too late to call FltCancelFileOpen or IoCancelFileOpen). The flag is set after the successful IRP_MJ_CREATE is completed back to the IO manager, in the IopParseDevice function. The documentation for ObOpenObjectByPointer implies that it's only safe to be called for FILE_OBJECTs that have this flag set. I should have mentioned this in my previous post on Duplicating User Mode Handles, but I can see I've only added an ASSERT but didn't explain it.

Thursday, June 14, 2012

The Flags of FILE_OBJECTs - Part II

In the post last week we discussed some of the various flags of the FILE_OBJECT, but since there are so many of them I left some for this week. So here we go again:
  • FO_SEQUENTIAL_ONLY - 0x00000020 - this flag is set to indicate that the caller guarantees that this FILE_OBJECT will only be used for sequential requests (no seeks). It maps to the FILE_SEQUENTIAL_ONLY flag and is usually set when Creating the handle, though it's possible to set and query it through the FileModeInformation information class. This flag is simply an indication to the cache manager that it can be more aggressive about removing things from the file system cache (since the flag implies that once data at a certain offset was touched it will likely not be needed again). If the user sets this flag but doesn't honor the contract (i.e. if IO isn't actually sequential (so it's at random offsets in the file)) then the cache performance will not be optimal. This flag (FILE_SEQUENTIAL_ONLY) is useful when copying a file or scanning a file for hashing and so on. Without the FILE_SEQUENTIAL_ONLY flag the file will be kept in the cache and will compete with everything else in there, so it might evict useful data from the cache (for example you have an application open and you switch to explorer and copy some files around... if the files that are copied are opened with the FILE_SEQUENTIAL_ONLY flag then the cache will likely prioritize existing things in cache over the file contents so when you switch back to your application it will still be in the cache. Without the flag it is more likely that the application will be removed from the cache because of the file copy...).
  • FO_CACHE_SUPPORTED - 0x00000040 - according to the MSDN page on the FILE_OBJECT structure this flag should only be set if the file system implements supports for the FSRTL_ADVANCED_FCB_HEADER structure. However, all windows file systems should implement this structure anyway. This flag is set by the filesystem as a means to keep track whether the open for this FILE_OBJECT had the FILE_NO_INTERMEDIATE_BUFFERING set (if FILE_NO_INTERMEDIATE_BUFFERING was set then this must be clear). This flag isn't terribly useful since the flag that controls most of the caching behavior is FO_NO_INTERMEDIATE_BUFFERING. I'm not sure why someone felt there was a need to add this flag when FO_NO_INTERMEDIATE_BUFFERING conveys the same information.
  • FO_NAMED_PIPE - 0x00000080 - this flag indicates that the FILE_OBJECT is for a named pipe. This is set by the file system (NPFS in this case) so it's not available in preCreate. According to a post on NTFSD, this flag can also be set for the connection to the IPC$ by the redirector, even though that FILE_OBJECT is not backed by NPFS on the remote server. On the local system everything works pretty much as you expect, with the flag being set for all NPFS FILE_OBJECTs.
  • FO_STREAM_FILE - 0x00000100 - this flag is set for FILE_OBJECTs created using the IoCreateStreamFileObject API (or friends: IoCreateStreamFileObjectEx, IoCreateStreamFileObjectEx2 and IoCreateStreamFileObjectLite). I've discussed stream FILE_OBJECTs in my post About IRP_MJ_CREATE and minifilter design considerations - Part V. Please note that this does NOT indicate the FILE_OBJECT is for an alternate data stream (I've seen some people confuse them). The two concepts (stream file objects and alternate data streams) are unrelated.
  • FO_MAILSLOT - 0x00000200 - this flag is similar in intent with the FO_NAMED_PIPE flag, except that in this case it indicates that the underlying file system is the mailslots file system (MSFS). According to the OSR thread that I pointed to earlier, apparently for FILE_OBJECTs for remote file systems the redirector doesn't set the FO_MAILSLOT flag even if the remote FILE_OBJECT has it. But anyway for local FILE_OBJECTs this flag should be set just like the FO_NAMED_PIPE flag.
  • FO_GENERATE_AUDIT_ON_CLOSE - 0x00000400 - as the MSDN page on the FILE_OBJECT structure points out this flag is deprecated. It has the same value as the next flag, FO_QUEUE_IRP_TO_THREAD.
  • FO_QUEUE_IRP_TO_THREAD - 0x00000400 - the only information I've been able to find has also been the MSDN page on the FILE_OBJECT structure. According to that page it means that IRPs will not be queued to this FILE_OBJECT and will be queued to the thread instead (at least that's what I get from the name). This has to do with the thread agnostic IO that was a new feature for Vista. The idea is that instead of queuing IO to a thread there are cases where it can be queued to a FILE_OBJECT (as an optimization). This is described in more detail in a post by PaulSli on Doron's blog, New for Windows Vista: thread agnostic I/O. My guess is that this flag disables that optimization. I've seen this flag set occasionally but I've never had to figure out where it's set and under what circumstances that happens. Still, I hope that this bit of investigation helps.
This is it for today, these things take quite a while to investigate so I can't do too many of them at once.

Thursday, June 7, 2012

The Flags of FILE_OBJECTs - Part I

In this set of posts I'd like to discuss the various flags defined for FILE_OBJECTs and how they are used in the system. The reason I'm doing this is because for most of them the documentation is scarce or wrong and I've been meaning to document them for a while. Please note that most of this information is based on my experience with them and is not meant to be an exhaustive list of how they might be used.

The only documentation I've found on MSDN is on the page about the FILE_OBJECT structure, but as I said before it's very scarce on the details. So without further delay let's start with the flags:

  • FO_FILE_OPEN - 0x00000001 - this flag is documented as being deprecated. The sample file systems (FastFat, CDFS) don't even mention it anywhere. I've also been unable to find it set anywhere (I expected either the file system to set it or the IO manager to set it but as far as I can tell it's not really used at all).
  • FO_SYNCHRONOUS_IO - 0x00000002 - this is a pretty complicated flag (in that it controls different types of behavior) . It can only be set during the create operation (before the FILE_OBJECT is sent through the IO stack) if the caller uses the FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT options. If none of these is set then the FILE_OBJECT won't have the flag set. This flag controls a couple of different behaviors:
    • if the flag is set then the FILE_OBJECT->CurrentByteOffset field is updated after each IO operation to reflect the current file position pointer and also it enables using the FILE_OBJECT->CurrentByteOffset as the current file offset to perform IO at (FILE_USE_FILE_POINTER_POSITION can only be used for a file that has FO_SYNCHRONOUS_IO set). So in a way this flag controls whether the current position can be used for this FILE_OBJECT.
    • if the flag is set then the IO manager will try to synchronize operations using that FILE_OBJECT. So if multiple threads use the same FILE_OBJECT then the IO manager will only allow one operation at a time to proceed. Please note that this doesn't mean that file system filter will only see one operation at a time since it's possible that other components on the system issue IO directly on the FILE_OBJECT without synchronizing with the IO manager (pretty typical with the Cache Manager).
    • If the flag is set then IoIsOperationSynchronous (and its friend FltIsOperationSynchronous) will return TRUE (as long as this isn't an asynchronous paging IO request, in which case it doesn't matter whether FO_SYNCHRONOUS_IO is set). The return value of IoIsOperationSynchronous and FltIsOperationSynchronous might have deeper impact on how the request is processed by the file system or file system filters (which might decide to process it inline rather than queue it if it's synchronous).
  • FO_ALERTABLE_IO - 0x00000004 - as the FILE_OBJECT structure documentation page mentions, this flag controls how the IO manager waits when using the FILE_OBJECT. If the flag is set then most waits (which can be waits to acquire the FILE_OBJECT to synchronize IO on it or just waiting for IO to complete) are alertable. The flag will be set depending on the presence of FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT. The flag is only meaningful for FILE_OBJECTs for which FO_SYNCHRONOUS_IO is set and can be set and cleared in the Create path and using the FileModeInformation information class. Please note that setting or clearing it using FileModeInformation only works if the FILE_OBJECT has the FO_SYNCHRONOUS_IO set. Another thing to note is that file systems seem to ignore this flag.
  • FO_NO_INTERMEDIATE_BUFFERING - 0x00000008 - this flag indicates whether the file was opened for non-cached IO. It maps to the FILE_NO_INTERMEDIATE_BUFFERING flag and can only be set during Create. It controls two different behaviors as well:
    • If it is set then there is no file system caching for this FILE_OBJECT. The implications here are pretty heavy. A lot of the cache management specific operations and flags are disabled (I'll discuss this more further down when I talk about some of the other flags). Also the file system code paths are quite different when dealing with cached IO and non-cached IO. This also means that for IO happening on this FILE_OBJECT the file system might need to perform flushes to keep the cached and non-cached views of the data coherent.
    • Another implication of this flag being set is that IO must be aligned to the sector size of the underlying device. This impacts reads and writes as well as setting the current file pointer for example. Unaligned operations on this FILE_OBJECT will generally fail at the IO manager level with STATUS_INVALID_PARAMETER.
  • FO_WRITE_THROUGH - 0x00000010 - this flag indicates that any data written on this FILE_OBJECT should be flushed immediately to disk. It maps to the FILE_WRITE_THROUGH flag. It can be set in the Create file path and also at any time with the FileModeInformation information class. This flag cannot be set when FO_NO_INTERMEDIATE_BUFFERING is set (it makes no sense if there is no cache at all). Please note that this flag doesn't require that operations be aligned to the underlying sector size, since caching is still in effect, it's just that writes are immediately flushed to disk.

I'll stop here for today but I plan to address all the flags in the following posts.