How to handle custom reparse points?

Hi all,

I have some text files with custom reparse points in ‘REPARSE_GUID_DATA_BUFFER’ format but there are error message prompts when they are opened by WordPad. I want to have a minifilter able to handle the custom reparse points and allow WordPad to open the file normally. The custom reparse points only contain some meta data and I know how to get them from user mode. I just need the file filter to ignore them and allow apps like Wordpad to work.

From the post: How can I get Repase Point Information In IRP_MJ_CREATE? (https://community.osr.com/discussion/comment/49056#Comment_49056)
It seems that I should handle “STATUS_REPARSE” in post-create, but different error prompt if I set Data->IoStatus.Status = STATUS_SUCCESS in post-create. Should I remove the reparse point in pre-create so that WordPad can open/read the file and add that back in at close? It sounds not ideal, but what is the best/easiest way of doing it?

Thanks in advance.

When you get the STATUS_REPARSE back the file hasn’t been opened. Off the top of my head I’d be inclined to have code in post create which looks for your data and then if you are not interested adds/strips the bits about handling reparse point in the CBC and then calls FltReissueSynchronousIo, the. Just carrying on as usual

My apologies that this is short on details, I’m effectively away from the internet…

Thank you for prompt response. It works for the first time but failed after I closed the file and then reopen the same file by WordPad. Let me layout what I did in pre-create and post-create:

In pre-create:
if (createOptions & FILE_OPEN_REPARSE_POINT) {
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}

In post-create:
if ((Data->IoStatus.Status == STATUS_REPARSE) && FLT_IS_IRP_OPERATION(Data) ) {
if ((Data->Iopb->Parameters.Create.Options & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT) {
Data->Iopb->Parameters.Create.Options |= FILE_OPEN_REPARSE_POINT;
FltSetCallbackDataDirty(Data);
FltReissueSynchronousIo(FltObjects->Instance, Data);
}
}

Any idea why it cannot work consistently? Thanks.

I’d need to play with it and I’m not in a position to this week. ‘Fails next time’ means what? Bsod? sharing violation?

My suggestion is that you wind back and try individual creates and step through it in the debugger watching what happens, what fields have values, what specific statuses and so on.

Word pad is simple, but it’s not as simple as FILETEST (Zezula.net somewhere) which allows you to craft individual operations and send them down one at a time.

. If you do this on an individual volume and just attach to it you won’t get drowned in noise. Make sure explorer isn’t active to reduce noise further and turn off WFD which can confuse things

‘Fail next time’ means the error message prompts when I try to open the same file with WordPad again. Just like it cannot handle the reparse point.
Does it has to do with “FILE_CREATED” in IoStatus.Information? If so, should I “FltReissueSynchronousIo” again if “FILE_CREATED”?

Again, I’m afi, but I have suspicion that the Information field values are different when you get status_reparse…

I also have a vague memory that there is a decent tutorial up on the learn.Microsoft.com pages…

As Rod is hinting you have a bug somewhere and just need to track it down. Highly recommend a ProcMon trace as that should make it clear what’s happening.

I also highly recommend switching to using the reparse point ECP if you can deal with the O/S restrictions. It’s significantly faster and easier to deal with than the STATUS_REPARSE/reissue dance. FltMgr exposes it via FltAddOpenReparseEntry:

https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/nf-fltkernel-fltaddopenreparseentry

Docs are terrible but you basically call this in PreCreate to let NTFS know which reparse point you handle. In PostCreate if you see OPEN_REPARSE_POINT_TAG_ENCOUNTERED set then you know it’s your reparse point. No mucking with STATUS_REPARSE or reissuing the I/O.

It sounds promising in switching to use ECP. The question is that can I set / get in user mode as I did with ‘REPARSE_GUID_DATA_BUFFER’?
I was using CreateFile with ‘FILE_FLAG_OPEN_REPARSE_POINT’ and then DeviceIoControl with ‘FSCTL_GET_REPARSE_POINT’ or ‘FSCTL_SET_REPARSE_POINT’ to work with reparse points. I was wondering if it’s possible to work with ECP in the same way? If not, what is the correct way to deal with ECP in user mode?
Thank you.

Yes, all the other user mode operations are the same. This is just an optimized way to hint which reparse points you’re interested in handling in the IRP_MJ_CREATE path. Saves the overhead of NTFS returning STATUS_REPARSE just to have the IRP_MJ_CREATE immediately reisssued.

From minispy of Windows driver samples, I was expecting my custom reparse point to be one of the element on the ECP list got from “FltGetEcpListFromCallbackData” in pre-create but unfortunately no (after I tried to run “minispy” on one of the file with custom reparse point).
- Shouldn’t my reparse point be one of the ECP?
- Is there a similar function available to determine whether my file has custom reparse points or not?

About “FltAddOpenReparseEntry”, should I build an ECP list and call this function to add that in pre-create even when I am working with files without reparse points? I am confused how I can “call this in PreCreate to let NTFS know which reparse point I handle”.
If possible, please help with more details. Thank you.

@Cal said:
From minispy of Windows driver samples, I was expecting my custom reparse point to be one of the element on the ECP list got from “FltGetEcpListFromCallbackData” in pre-create but unfortunately no (after I tried to run “minispy” on one of the file with custom reparse point).
- Shouldn’t my reparse point be one of the ECP?
- Is there a similar function available to determine whether my file has custom reparse points or not?

In PreCreate the file isn’t yet open so no one can possibly know if it has your reparse point or not.

About “FltAddOpenReparseEntry”, should I build an ECP list and call this function to add that in pre-create even when I am working with files without reparse points? I am confused how I can “call this in PreCreate to let NTFS know which reparse point I handle”.
If possible, please help with more details. Thank you.

You call FltAddOpenReparseEntry in PreCreate to indicate which reparse point you handle:

openReparseEntry->Size       = sizeof(OPEN_REPARSE_LIST_ENTRY);
openReparseEntry->ReparseTag = IO_REPARSE_TAG_FOOOOO;

RtlCopyMemory(&openReparseEntry->ReparseGuid,
              &GUID_REPARSE_TAG_FOOOOO,
              sizeof(GUID));

status = FltAddOpenReparseEntry(FooGlobals.FilterHandle,
                                Data,
                                openReparseEntry);

if (!NT_SUCCESS(status)) {

    FooTracePrint(ERROR, "FltAddOpenReparseEntry failed! 0x%x\n",
                       status);

    goto Exit;

}

*CompletionContext    = openReparseEntry;

And then in PostCreate you check to see if it was acknowledged:

if (!BooleanFlagOn(openReparseEntry->Flags,
                   OPEN_REPARSE_POINT_TAG_ENCOUNTERED)) {

    //
    // Did not encounter our reparse point
    //
    goto Exit;

}

No STATUS_REPARSE, no reissuing the I/O, etc.

Thank you for the reply.
I have tried to put the codes in the pre-create and post-create. An instance of OPEN_REPARSE_LIST_ENTRY is added as one of the global objects and it was successful at FltAddOpenReparseEntry. However, I have tried to use WordPad to open the file with custom reparse points for a number of times, but only about 50% or less were successful. I was wondering if I should do something special in IRP_MJ_QUERY_INFORMATION or IRP_MJ_READ other than create?

OK, you have a bug ? But you don’t have any detail here so it’s hard to know how to help…

What does success vs not success mean? You can open it? Save it? Print it?

Can you open the file with FileTest?

Do you have a ProcMon trace?

One other thing: Are you calling FltRemoveOpenReparseEntry?

(And FYI I have a minifilter that handles custom reparse points running in production…The entire create module is ~300 lines including comments and module header, so there really isn’t much else you have to do with IRP_MJ_CREATE.)

I must not working with open_reparse_list_entry correctly. I did not call FltRemoveOpenReparseEntry at all. I’ll test with FileTest. Is it possible for you to address more on how to use open reparse list entry? Thank you.

Wordpad failed to open the file with error prompt: An unexpected error occurred while reading C:\xxx\text.txt, and I have close WordPad and retry.

There’s really nothing else to it…You call FltAddOpenReparseEntry in Pre and FltRemoveOpenReparseEntry in Post. With just that in place you should be able to open your reparse points as normal files without any additional processing.

I added FltRemoveOpenReparseEntry in Post and I have tried with FileTest. “CreateFile” will success if my filter is running and not if I stopped my filter. Which is good. However, it still behaves the same if I use WordPad to open/close the file repeatedly. I compared some ProcMon traces (with filter of my file’s name) either Wordpad is able or unable to open the file. The difference is that many “QueryStandardInformationFile” and “ReadFile” are present when Wordpad is able to open the file while they are not there if failed to open.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Below are cropped from “able-to-open” ProcMon:

CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, AllocationSize: n/a, OpenResult: Opened
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:15:47 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
CloseFile	C:\Target\noteRP.txt	SUCCESS	
QueryDirectory	C:\Target\noteRP.txt	SUCCESS	FileInformationClass: FileBothDirectoryInformation, Filter: noteRP.txt, 2: noteRP.txt
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:15:47 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
QueryStandardInformationFile	C:\Target\noteRP.txt	SUCCESS	AllocationSize: 8, EndOfFile: 7, NumberOfLinks: 1, DeletePending: False, Directory: False
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:15:47 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
CloseFile	C:\Target\noteRP.txt	SUCCESS	
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 5, Priority: Normal
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 4
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 2
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 2
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 2
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 2
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 2
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 7
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 4
CloseFile	C:\Target\noteRP.txt	SUCCESS	
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, Write, AllocationSize: n/a, OpenResult: Opened
ReadFile	C:\Target\noteRP.txt	SUCCESS	Offset: 0, Length: 2, Priority: Normal
CloseFile	C:\Target\noteRP.txt	SUCCESS	

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Below are cropped from “failed-to-open” ProcMon:

CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryNetworkOpenInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:25:05 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, AllocationSize: 8, EndOfFile: 7, FileAttributes: ARPNCI
FileSystemControl	C:\Target\noteRP.txt	SUCCESS	Control: FSCTL_CREATE_OR_GET_OBJECT_ID
QueryObjectIdInformationVolume	C:\Target\noteRP.txt	SUCCESS	ObjectId: 62247B2074077641A82DDAD298218674
QueryNameInformationFile	C:\Target\noteRP.txt	SUCCESS	Name: \Target\Level_0\noteRP.txt
QueryNameInformationFile	C:\Target\noteRP.txt	SUCCESS	Name: \Target\Level_0\noteRP.txt
QueryNormalizedNameInformationFile	C:\Target\noteRP.txt	SUCCESS	
CloseFile	C:\Target\noteRP.txt	SUCCESS	
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
FileSystemControl	C:\Target\noteRP.txt	SUCCESS	Control: FSCTL_CREATE_OR_GET_OBJECT_ID
CloseFile	C:\Target\noteRP.txt	SUCCESS	
CreateFile	C:\Target\noteRP.txt	REPARSE	Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, AllocationSize: n/a, OpenResult: <unknown>
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:25:05 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
CloseFile	C:\Target\noteRP.txt	SUCCESS	
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:25:05 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
CloseFile	C:\Target\noteRP.txt	SUCCESS	
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Synchronize, Disposition: Open, Options: Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
FileSystemControl	C:\Target\noteRP.txt	SUCCESS	Control: FSCTL_CREATE_OR_GET_OBJECT_ID
CloseFile	C:\Target\noteRP.txt	SUCCESS	
QueryDirectory	C:\Target\noteRP.txt	SUCCESS	FileInformationClass: FileIdBothDirectoryInformation, Filter: noteRP.txt, 2: noteRP.txt
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:25:05 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
CloseFile	C:\Target\noteRP.txt	SUCCESS	
CreateFile	C:\Target\noteRP.txt	REPARSE	Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: N, ShareMode: Read, AllocationSize: n/a, OpenResult: <unknown>
CreateFile	C:\Target\noteRP.txt	SUCCESS	Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
QueryBasicInformationFile	C:\Target\noteRP.txt	SUCCESS	CreationTime: 3/14/2023 12:21:23 AM, LastAccessTime: 3/14/2023 2:25:05 PM, LastWriteTime: 3/13/2023 6:41:50 PM, ChangeTime: 3/14/2023 12:22:25 AM, FileAttributes: ARPNCI
CloseFile	C:\Target\noteRP.txt	SUCCESS	

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Note: There are also logs for “C:\Users.…\noteRP.txt.lnk” that I left out from both cases, not sure why there are “.lnk” version. I’ve seen them when I open any text file (without custom reparse points) by Notepad or Wordpad.

How are you creating the reparse point? What does “fsutil reparsepoint query” say about it when your filter isn’t running?

Also, what path is “\Target\Level_0\noteRP.txt”?

Sorry for causing confusion, the file name is actually C:\Target\Level_0\noteRP.txt, I was trying to make it easier but obviously it’s not helping.
“fsutil reparsepoint query” got the following when my filter is not running:
Reparse Tag Value : 0x00ffff00
GUID : {C2A9…-…-…-…-…}
Reparse Data Length: 0x6
Reparse Data:
0000: 33 00 33 00 00 00 3.3…

I was wondering if I can read the reparse point by code and remove it at Pre-Create, maybe add it back in at post-close? Not ideal, but might be a fallback solution.
not sure if there is any constraints for FltCreateFileEx and FltFsControlFile at both callback functions?