Last week I ran into a strange Windows file system behavior that I couldn’t find any information on anywhere. Since it’s always extremely frustrating to try to figure things out when there’s no information available, I thought I would share what I found. The bug we were experiencing had to do with a directory query operation over the network (e.g., when you run ‘dir \localhostc$Windows’ from a command window). If the directory doesn’t have many files in it this works just fine, but if it is a large directory, as in the example above then the IRPs that are issued to the file system are a bit strange. Our filter driver wasn’t handling these quite correctly, and the result was that if you queried the directory using the local name you’d get ~200 files, and if you used the UNC name you’d only get about ~150 files.
After digging into this with a coworker, we found an unexpected style of IRP. When performing the directory query over the network the SRV kernel component issues a IRP_MN_QUERY_DIRECTORY with a IrpSp->Parameters.QueryDirectory.FileName and IrpSp->Parameters.QueryDirectory.FileIndex combination that seems to essentially reset the point at which the enumeration continues. The sequence we were seeing goes something like this:
- Normal IRP_MN_QUERY_DIRECTORY with FileName of “*” and SL_RESTART_SCAN. This is successful and returns the first buffer of information.
- Normal IRP_MN_QUERY_DIRECTORY with no FileName (and no FileIndex). This is successful and continues where the previous scan left off.
- Repeat #2 approximately 3 times.
- Unexpected IRP_MN_QUERY_DIRECTORY with FileName = “AppPatch” (one of the entries that was previously returned in #2, FileIndex = 0, and Flags = SL_INDEX_SPECIFIED. This appears to enumerate beginning with the entry just after “AppPatch”.
- Repeat #2 through #4 until there are no more entries.
The behavior doesn’t appear to be documented anywhere, but I was able to reproduce it by rolling my own IRPs that match the above. (Zw APIs don’t accept the FileIndex parameter, so I couldn’t see any way to reproduce it outside of IRP rolling.)
It seems likely that the SRV component is sending QUERY_DIRECTORY IRPs until it has enough data to fill a network buffer, and then uses the restart at named index capability to get the data to start filling the next network buffer. Of course, that is just speculation, but it seems to make a kind of sense.
What I have derived from this is that there are two ways to use the SL_INDEX_SPECIFIED flag. If there is a FileName provided along with the flag, then the FileIndex value seems to be ignored, and the provided filename is used to find the index at which enumeration should begin. If the FileName is NULL, then the FileIndex would be used instead. Again, this is speculation but seems to be borne out by my testing.