FltGetFileNameInformation error 0XC01C0005

Hi
I have to maintain the records of the files created or modified on a USB drive.
I wrote a mini filter driver and tried to get the name of the file in FilterPreClose using FltGetFileNameInformation() but it failed with error 0XC01C0005.
Why is it so?
Please help.

Thanks and Regards
Utsav

From NTstatus.h

#define STATUS_FLT_INVALID_NAME_REQUEST ((NTSTATUS)0xC01C0005L)

From The Fine Manual

STATUS_FLT_INVALID_NAME_REQUEST
One of the following:
FltGetFileNameInformation cannot get file name information if the
TopLevelIrp field of the current thread is not NULL, because the
resulting file system recursion could cause deadlocks or stack overflows.
(For more information about this issue, see IoGetTopLevelIrp.)
FltGetFileNameInformation cannot get file name information in the paging
I/O path.
FltGetFileNameInformation cannot get file name information in the
post-close path.
FltGetFileNameInformation cannot get the short name of a file in the
pre-create path.

I’d bet it is the first or (less likely) the second…

wrote in message news:xxxxx@ntfsd…

Hi
I have to maintain the records of the files created or modified on a
USB drive.
I wrote a mini filter driver and tried to get the name of the file
in FilterPreClose using FltGetFileNameInformation() but it failed with
error 0XC01C0005.
Why is it so?
Please help.

Thanks and Regards
Utsav

How do I get the file name in case TopLevelIrp field of the current thread is not NULL?

You don’t… you can only do the query during Pre-Create/Post-Create and keep that name in a stream context (or
a handle context, but that will hardly suffice).

xxxxx@gmail.com wrote:

How do I get the file name in case TopLevelIrp field of the current thread is not NULL?


NTFSD is sponsored by OSR

For our schedule of debugging and file system seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer


Kind regards, Dejan (MSN support: xxxxx@alfasp.com)
http://www.alfasp.com
File system audit, security and encryption kits.

I just checked, in my case TopLevelIrp field of the current thread is NULL as returned by IoGetTopLevelIrp() even then FltGetFileNameInformation() fails.

What are the parameters for you call to FltGetFileNameInformation() ?

Also, I don’t agree with Dejan that that’s the best approach. The name infrastructure in FltMgr (when you use FltGetFileNameInformation() instead of building the name yourself) can be used not only during IRP_MJ_CREATE and it has it’s own cache, complete with invalidating it at the right time.

Thanks,
Alex.

> I just checked, in my case TopLevelIrp field of the current thread is NULL

as returned by IoGetTopLevelIrp() even then FltGetFileNameInformation()
fails.

And the paging bit? (although I’d agree with Alex that bad parameters is
also a likely possibility - as often has to be the case, the documentation
does not give all the possible reaspms for failure)

Alex,

The name infrastructure in FltMgr (when you use
FltGetFileNameInformation() instead of building the name yourself)
can be used not only during IRP_MJ_CREATE and it has it’s own cache,
complete with invalidating it at the right time.

OTOH an API which cannot be relied on when you need it is a bit of a
chocolate teapot, and you can be sure that in stress testing you’ll find a
hitherto unknown path in which IoTopLevelIrp is set. If you know you are
going to need the name eventually, why not set a reference to the name
object in PostCreate and PostRename and then it’s there when you need it.
Assuming that the FltMgr’s name caching is implemented in the way one would
expect it to be it shouldn’t cost much (if any) extra memory and you’ll not
end painted into a corner.

Further FileInfo seems to send down a FltGetFileNameInformation after every
create so you’ll not even hit the file system all that often (all other
things being equal).

Rod, i’m not sure i would describe the name APIs as APIs which cannot be relied on when you need them but rather as APIs that don’t work in all cases where a user might think they need them but they might not in fact need them. For example it’s rather common that people ask for the ability to call FltGetFileNameInformation during paging writes but I’m not convinced that is necessary. Of course, the APIs are more limiting than the ideal “this function will always work”, but then so are the memory allocation APIs or some of the synchronization primitives and so on. I wrote a blog post about using names in file system filters that explains in more detail how i view this issue (http://fsfilters.blogspot.com/2010/02/names-and-file-systems-filters.html).

Setting the reference to the name is a good way to keep a name around instead of copying or something but knowing when the name has become invalid is not that easy. You listed postCreate and postRename but in my oppinion that’s an oversimplication. What about overwriting renames? Hardlink creation ? Overwriting hardlink creation? Directory rename (which invalidates all the names in all the files under that directory) ? What about setting a short name on a file or directory which might invalidate some names (opened names) but not others (normalized names)? FltMgr’s name caching is implemented to take these cases into account, but keeping a reference to a name obtained at some point in the past doesn’t guarantee that the name is still accurate. Basically, calling FltGetFileNameInformation() every time is the right way to do it because if FltMgr has seen a condition that required invalidation of the name then you’ll get a new name. Also, the name is cached so it won’t cost you much more in terms of performance than keeping your own reference.

The real question when trying to get a name is “what is this going to be used for”? Perhaps the OP can explain why they need a name for a file in preClose ?

Thanks,
Alex.

> Directory rename (which invalidates all the names in all the files under

that directory) ?

That’s a good point. As for the rest, I know that I have had significant
issues with late breaking issues because FltGetFileNameInfo would suddenly
fail under some weird circumstance.

But I was younger and far less well versed in all these nuances in those
days. Recently I have used FltGetFileNameInfo extensively with no issues
(although I am *extremely* careful about where I call it).

Of course, getting a name is a bit of a lottery in a filter anyway since you
cannot know that the name is instantaneously valid with holding locks over
inadvisable operations, so it really does get back to what the OP needs the
name for - Logging would be easy, being able to reopen the file and be sure
that it is the same one, less so.

Here is what I am doing

FLT_PREOP_CALLBACK_STATUS
FilterPreClose(__inout PFLT_CALLBACK_DATA Data,
__in PCFLT_RELATED_OBJECTS FltObjects,
__deref_out_opt PVOID *CompletionContext)
{
PFLT_FILE_NAME_INFORMATION nameInfo;
NTSTATUS status;
ACCESS_MASK desiredAccess;
ULONG options;
FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
PIRP pirp = NULL;

UNREFERENCED_PARAMETER( CompletionContext );

PAGED_CODE();

DbgPrint(“\nFilterPreClose\n”);
pirp = IoGetTopLevelIrp();
if (pirp)
{
DbgPrint(“Top Level IRP is not NULL\n”);
}
else
{
DbgPrint(“Top Level IRP is NULL\n”);
}

status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo );
if (!NT_SUCCESS( status ))
{
DbgPrint(“FltGetFileNameInformation failed: 0X%X”, status);
return returnStatus;
}

FltParseFileNameInformation(nameInfo);

options = Data->Iopb->Parameters.Create.Options;
desiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;

DbgPrint(“File: %wZ\n”, &nameInfo->Name);
DbgPrint(“Options: 0X%X\n”, options);
DbgPrint(“Access: 0X%X\n”, desiredAccess);

FltReleaseFileNameInformation(nameInfo);
return returnStatus;
}

As I wrote earlier that I want to log the filenames that are created or modified on a usb disk drive.

> status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED |

FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo );

That looks pretty fine to me. I’m hoping Alex will be able to provide some
assistance (I expect that this might be another of those edge cases that the
doc doesn’t mention and one of the reasons why I am leery of this API).

Some more questions:

  • Is this sometimes or always?
  • If sometimes can you capture a stack in a failure case? (I don’t suppose
    that surprise removal is todo with it?)
  • What OS? Is it just one or many operating systems.

As to your requirements:

I have to maintain the records of the files created or modified on a USB
drive.

So to poke down a level:

  • Every change or that it has been changed? I’d guess the latter.
  • How accurate does the name have to be (if the name was changed at the same
    time that the file was changed which name do you report)?
  • In general what are your requirements about rename
  • How accurate does the reporting have to be (for instance if a writeable
    section was created will reporting that do, or do you need to know only that
    it might be modified)
  • Related to accuracy is the tradeoff between noting change in the post call
    (when you know its happened) and the pre call (when you hope it will) and
    the tie in to surprise removal.

I tend to eschew doing things in the close path because the file object is
in a bad state and you may have picked up a certain in me about
FltGetFileNameInformation. My inclination would be to note the name as soon
as the change has happened/is about to happen (preSetInfo, postCreate,
preWrite, preAcquireForSection, preFsCtl and so on). If the precise
requirements do not allow this I would postpone to preCleanup (last handle
closed) with (if required) some magic to handle paging writes on writeable
section happening after the file handle is closed. Your precise
requirements will obvious drive the decision.

  • Is this sometimes or always?
    Always
    -What OS?
    XP SP2
  • Every change or that it has been changed?
    “that it has been changed”
  • How accurate does the name have to be (if the name was changed at the same time that the file was changed which name do you report)?
    Any (New name will be better)
  • In general what are your requirements about rename
    No requirement

Now what I am trying is
-get the filename in POSTCREATE
-if it is on USB device get the Options and DesiredAccess
options = Data->Iopb->Parameters.Create.Options;
desiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
-check for FILE_WRITE_DATA and FILE_APPEND_DATA in desiredAccess and maintain the log if any is present.

this seems to work fine

I don’t want the directory entries so I used FltIsDirectory() to differentiate between files and directories but this call says “directory” for all files and directories. How to differentiate?

Got it. It was my fault.
I took BOOL(int) instead of BOOLEAN(byte) hence the garbage value and error.

I agree with Rod’s advice.

I don’t see anything wrong with the way you’re calling the function, but I decided to try it out for myself and running with a minifilter that does something similar to what you do fails in all the cases where FO_CLEANUP_COMPLETE is set (which is usually the case in preClose). This is the code i see (Win7):

fltmgr!FltGetFileNameInformation+0xa0:
9601cf18 8b45f8 mov eax,dword ptr [ebp-8] // get FileObject
1: kd>
fltmgr!FltGetFileNameInformation+0xa3:
9601cf1b f7402c00400000 test dword ptr [eax+2Ch],4000h // check whether FO_CLEANUP_COMPLETE is set in the FileObject->Flags
1: kd>
fltmgr!FltGetFileNameInformation+0xaa:
9601cf22 7521 jne fltmgr!FltGetFileNameInformation+0xcd (9601cf45)
1: kd>
fltmgr!FltGetFileNameInformation+0xcd:
9601cf45 8b450c mov eax,dword ptr [ebp+0Ch] // load the NameOptions parameter
1: kd>
fltmgr!FltGetFileNameInformation+0xd0:
9601cf48 2500ff0000 and eax,0FF00h // keep only the FLT_VALID_FILE_NAME_QUERY_METHODS
1: kd>
fltmgr!FltGetFileNameInformation+0xd5:
9601cf4d 3d00030000 cmp eax,300h // is the method FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY ?
1: kd>
fltmgr!FltGetFileNameInformation+0xda:
9601cf52 7479 je fltmgr!FltGetFileNameInformation+0x155 (9601cfcd) // not taken
1: kd>
fltmgr!FltGetFileNameInformation+0xdc:
9601cf54 3d00010000 cmp eax,100h // is the method FLT_FILE_NAME_QUERY_DEFAULT ?
1: kd>
fltmgr!FltGetFileNameInformation+0xe1:
9601cf59 7472 je fltmgr!FltGetFileNameInformation+0x155 (9601cfcd) // it is , jump is taken
1: kd>
fltmgr!FltGetFileNameInformation+0x155:
9601cfcd b805001cc0 mov eax,0C01C0005h // fail…

So the way i read this is : If FO_CLEANUP_COMPLETE is set and the caller requested FLT_FILE_NAME_QUERY_DEFAULT or FLT_FILE_NAME_QUERY_FILESYSTEM_ONLY then fail it with 0C01C0005h. I think for your purposes querying the name in postCreate should be fine (or even preCleanup but not later than that since it will start running into this issue). Also, i’m not sure whether you need the normalized name, in general for logging the Opened name works (since humans can parse short names fairly well if there happens to be any in the path). In general Normalized names are only necessary for comparisons with other names.

There is another thing wrong in the code you posted. You can’t use Data->Iopb->Parameters.Create.Options except during an IRP_MJ_CREATE. You should use the appropriate FLT_PARAMETER member structure for the operation you’re currently processing. The same code works when doing it in postCreate because it’s an IRP_MJ_CREATE and Parameters.Create is the right structure to use.

Thanks,
Alex.