As a file system filter developer, one of the great pains in life is when a file system operation fails deep in the bowels of the file system. For example, say I’m trying to rename a file with FltSetInformationFile for FileRenameInformation and I get back STATUS_ACCESS_DENIED. How do I track that down? Sure, I could try single stepping through the function until I see a STATUS_ACCESS_DENIED, but that could take quite a while. Even worse, if the file system is NTFS I will undoubtedly end up stepping into some other thread and losing the thread context of my failing rename.
Enter NTSTATUS debugging! Since at least Windows 7, NTFS has had a handy undocumented feature that can cause a breakpoint when NTFS is about to return a particular NTSTATUS. Prior to Windows 10 you enable this feature using the following WinDbg command:
eb Ntfs!NtfsStatusDebugEnabled 1
On Windows 10 and later you enable this feature using a different command:
ed Ntfs!NtfsStatusDebugFlags 2
Now you’re ready to set the status that you want to break on. You do that with the following:
ed Ntfs!NtfsStatusBreakOnStatus 0xc0000022
Where the supplied value is whatever NTSTATUS value you want to break on in the debugger. Once NTFS is about to return that particular value from any internal function, you’ll hit a breakpoint:
Assertion failure - code c0000420 (first chance) Ntfs!NtfsStatusTraceAndDebugInternal+0x20c: fffff801`3640e0f4 int 2Ch kd> k Child-SP RetAddr Call Site fffffe0f`cf03d550 fffff801`36494b5a Ntfs!NtfsStatusTraceAndDebugInternal+0x20c fffffe0f`cf03d5a0 fffff801`364962b1 Ntfs!TxfAccessCheck+0x2da fffffe0f`cf03d700 fffff801`36495cd1 Ntfs!NtfsAccessCheck+0x281 fffffe0f`cf03d930 fffff801`364959ea Ntfs!NtfsCheckExistingFile+0xd1 fffffe0f`cf03d9d0 fffff801`3649502c Ntfs!NtfsOpenExistingAttr+0xda fffffe0f`cf03da90 fffff801`364939ed Ntfs!NtfsOpenAttributeInExistingFile+0x3dc fffffe0f`cf03dc40 fffff801`364ebfb4 Ntfs!NtfsOpenExistingPrefixFcb+0x1ed fffffe0f`cf03dd20 fffff801`364ec4bb Ntfs!NtfsFindStartingNode+0xcc4
Of course, lacking source code to NTFS this doesn’t exactly tell you why the error is being returned, but it at least gives you a place to start. Usually at this point I begin setting breakpoints farther and farther up the call stack until I can figure out where the working and non-working cases diverge (hey, no one ever said this was a glamorous job).
Note that this functionality is not in FAT, though at least with FAT you can (mostly) just look at the public version of the sources and figure out the possible reasons for the failure.