How to handle custom reparse points?

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?

That doesn’t look like a valid reparse tag (see the IsReparseTagValid macro in ntifs.h). Not sure if that’s the problem but it certainly can’t help.

No, removing and putting back the reparse point would be a very bad design and is not necessary. You’d basically need to open every single file in PreCreate to know if it’s your reparse point or not. And then you’d be thrashing the disk adding and removing the reparse point for no reason. The inbox Cloud Filter uses this feature so it definitely works.

Sorry to say you need to keep debugging to figure out what’s going on. Is WordPad the ONLY app that doesn’t work? Do you see opens for your reparse point in your filter (by path) that are NOT acknowledged? Note that your WordPad ProcMon trace has a STATUS_REPARSE result which would indicate that you’re not capturing all opens of your reparse point.

(I’ll also say that you might want to invest in taking a seminar or getting some consulting help. Not sure what the ultimate goal of all this is but you want to make sure you understand how all of this is supposed to work before plowing forward to an implementation. If it’s just for learning then whatever, but if you’re trying to build a product it’s a good to take a step back before you get too far down a rat hole.)

In order to avoid conflict with reserved reparse tag in Microsoft, I just choose a different number for reparse tag. GUID is random-generated and I just didn’t type all of it down. Reparse data is simply 33. I’ll check if “IsReparseTagValid” but it might be okay since it works fine by FileTest.
I am seeing the same behavior using Notepad as well, so it is a general issue here. My filter driver is simple and similar to the minifilter sample. Maybe I should strip everything else down and just test the code you posted.
Another related question: Can I call FltFsControlFile to FSCTL_SET_REPARSE_POINT and FSCTL_GET_REPARSE_POINT in pre-create?

@Cal said:
Maybe I should strip everything else down and just test the code you posted.

I think that’s a good idea.

Another related question: Can I call FltFsControlFile to FSCTL_SET_REPARSE_POINT and FSCTL_GET_REPARSE_POINT in pre-create?

No, those require an opened File Object (i.e. FsContext != NULL) and the File Object is not open in PreCreate.