I know there are a bunch of articles about using reparse points which I have read but I am still having troubles with this. I am hoping that someone can point me in the right direction here. I am not sure what I am missing in order to get this implemented correctly.
I created a new REPARSE_GUID_DATA_BUFFER and attached it to a file. I am implementing a custom reparse point since I am needing to create the backing file, and the simple symlink will not work for my case.
In my IRP_MJ_CREATE pre operation I do not handle any reparse logic but just return FLT_PREOP_SUCCESS_WITH_CALLBACK. In post operation I see that data->IoStatus.Status == STATUS_REPARSE and data->IoStatus.Information is my tag. So I can parse the TagData which is already set and can now open the new file and assign the TargetFileObject to the new file and call FltReissueSynchronousIo. From my understanding since I handle the reparse I should return set the IoStatus.Status to STATUS_SUCCESS.
Unfortunately the file with the reparse data attached does not open correctly. Using notepad I get error that it cannot find the file.
I tried to compare in process monitor the difference when I am handling the reparse vs a traditional symlink. From what It looks like I am returning STATUS_SUCCESS since I am handling this reparse point, where as the traditional symlink is returning the STATUS_REPARSE. I am then seeing that after the create, QueryInformationVolume is being called and returning INVALID_DEVICE_REQUEST. In order to open the file am I required to implement IRP_MJ_QUERY_VOLUME_INFORMATION as well? I am assuming I am missing something that is causing QueryInformationVolume to be called in the first place.
In post create my code is like this with error handling omitted for readability.
Are you trying to reparse the open to another path? Or are you just handling your own custom reparse point data? It's not clear.
But, you shouldn't be replacing the File Object in any case:
If you're reparsing to another path you call IoReplaceFileObjectName and complete with STATUS_REPARSE.
If you're handling your own custom reparse point then you need to set FILE_OPEN_REPARSE_POINT before you reissue the open, otherwise the app is just going to get back STATUS_REPARSE and fail. You should be able to see this in ProcMon.
Also note that if this is a custom reparse point thing then better to use FltAddOpenReparseEntry in PreCreate and checking for OPEN_REPARSE_POINT_TAG_ENCOUNTERED in PostCreate instead of doing the STATUS_REPARSE/FltReissueSynchronousIo thing. It's much more efficient as long as you're only targeting Win10 1607 and later.
I started with IoReplaceFileObjectName but I could not redirect this across volumes. What I am trying to do is create and write the backing file which my be on a separate volume or eventually on a network share. Although for now if I can simplify this by making it local that would be ok too.
My understanding was in order do this correctly I needed to implement my own reparse point with a custom tag/guid.
I am currently parsing my buffer that I already set up but I think the issues is that I am not handling the redirection correctly. If you are saying that I do not replace the FILE_OBJECT what do I need to do to preform the actual redirection?
Currently in post create I see that the data->IoStatus.Status is set to STATUS_REPARSE and data->IoStatus.Information is set to my tag. I thinking that I am not processing the target correctly. I looked at symrep in the samples but this just calls IoReplaceFileObjectName to redirect the name. What do i need to do to perform the actul redirection if I am not replacing the FILE_OBJECT?
I will take a look at FltAddOpenReparseEntry but I think the same issue will still occur until I can process the redirection correctly. Do you happen to know of any sample that does something like this that I could look at?
So the user creates A.TXT and you also want to create B.TXT? Should the user application be interacting with B.TXT at all or is that just your own file managed by the driver?
Or is that if the user tries to create A.TXT you really want them to create B.TXT instead?
Sorry if this my use case is not clear. Let me try to restate this again.
The user tries to open a.txt (\Device\HarddiskVolume6\a.txt) they are not aware that b.txt (\Device\HarddiskVolume3\path\b.txt) exists at all. When they open a.txt the content can come from b.txt but the user should not know anything about the backing b.txt file. Ideally b.txt is owned by the driver but I am not needed to create a true isolation filter or anything that complex here. So if they could just open a.txt and the driver redirects it to b.txt (on a different volume) this would likely work for what I am trying to do.
Since the reparse point is already set on a.txt the post create sees that this the correct tag, and just needs to handle the redirection. My understanding was that just assigning data->Iopb->TargetFileObject and reissuing the IO should handle this but clearly I am missing something here.
No, it doesn't work that way. When the I/O Manager initially handles the create request for A.TXT it creates an unopened File Object (FsContext == NULL) for the open instance. If you swap in a new File Object and send it down the layers beneath you will see that new File Object but the layers above won't (including the I/O Manager).
Alternatively, when you complete with STATUS_REPARSE and a new name the I/O Manager starts back from the top and retries the open with the new name (i.e. standard symbolic link stuff). This is very easy but has limitations, mostly because the layers above you are aware of the redirection. So, for example, the application will see the target path if they call GetFinalPathNameByHandle on the HANDLE. Another weird artifact you get is if the file is flagged by A/V, which will notify the user that their file B.TXT is suspicious...And reparsing to different volumes can have other weird issues depending on the app and how exactly you're managing things.
If you want it to be entirely transparent to the layers above you then you need to start creeping into Isolation. This involves completing the user's open with SUCCESS and then servicing all the incoming I/O operations using a File Object you previously created with FltCreateFile. This if much more powerful but starts to get significantly more complicated so better if you can live with STATUS_REPARSE and its limitations.
Thanks @Scott_Noone_OSR for your pointers. I think dealing with isolation is out of the scope of what I am trying to build.
better if you can live with STATUS_REPARSE and its limitations
Are you referring to only being able to reparse to the same volume using IoReplaceFileObjectName or just using one of the existing Microsoft provided tags (and data) to let it do the right thing like IO_REPARSE_TAG_SYMLINK? I am assuming IO_REPARSE_TAG_SYMLINK will be able to handle cross volume reparse.
If I use IO_REPARSE_TAG_SYMLINK then do I do anything in create or do I just set the status to STATUS_REPARSE?
Are you referring to only being able to reparse to the same volume using IoReplaceFileObjectName or just using one of the existing Microsoft provided tags (and data) to let it do the right thing like IO_REPARSE_TAG_SYMLINK? I am assuming IO_REPARSE_TAG_SYMLINK will be able to handle cross volume reparse.
What I mean by "limitations" has more to do with application compatibility.
For example: you can't rename across volumes. That means if you reparse X:\A.TXT to Y:\B.TXT and then an app tries to rename the file to X:\C.TXT they'll get back a STATUS_NOT_SAME_DEVICE error...Unless they're doing the rename by calling MoveFileEx with MOVEFILE_COPY_ALLOWED set, in which case MoveFileEx will turn the error into a file copy. Some apps do and some apps don't, you don't really know until you try the apps you care about and see what happens.
Also like I said reparsing can make logs confusing because the logs will have the real path and not the logical path. This again may or may not matter, it just depends on the use case.
If I use IO_REPARSE_TAG_SYMLINK then do I do anything in create or do I just set the status to STATUS_REPARSE?
Not sure. I would start by trying to reparse it just like SimRep does (with IO_REPARSE) and not use IO_REPARSE_TAG_SYMLINK because it's not really an NTFS symlink.
Though I'll note the sample does this in PreCreate and I also only have experience doing it in PreCreate. Not sure if doing in PostCreate in response to hitting your own reparse point has any special considerations. It shouldn't, but again just haven't ever had to do it this way.
Thanks again for your help. This is starting to clear things up. The documentation does not explain this very well in my opinion.
When I looked at SymRep It was just setting the new name by using IoReplaceFileObjectName and setting setting the result to STATUS_REPARSE/IO_REPARSE appropriately. This makes sense on the same volume. You mentioned
That means if you reparse X:\A.TXT to Y:\B.TXT
Is it possible to use IoReplaceFileObjectName or is there a way to reparse across volumes. I understand what you are saying and why this may lead to issues but for my understanding I would like to see what is involved to get the reparse working across volumes.
I was able to use get this working before using IoReplaceFileObjectName in postcreate but only on the same volume. Am I missing something on how to get this to work across volumes other than handling every IRP internally which is much more than I think I want to handle here.
On another note, in precreate is there a way to know the FILE_OBJECT is a reparse point? I would like to not process other files and avoid having to handle them in post create if possible. I have tried querying the file using FltQueryInformationFile with FileBasicInformation to check the attributes but since the FILE_OBJECT is not opened I am unable to get this information. Is there another approach that I should be using to check?
Apparently I spoke too soon. If I use IoReplaceFileObjectName in post create the file is opened as expected but driver verifier is complaining that I am setting IO_REPARSE in the information field.
FILTER VERIFIER ERROR: A filter changed the create action (in IoStatus.Information) for a successful
create in the post-operation callback, and the new create action is incompatible
with the create disposition (in Iopb->Parameters.Create.Options).
(Filter = FFFFAA8A69BB8660 (myfilter), Routine = FFFFF80379926FB0, Disposition = 1, Action = 0
I am not sure how to get around this verifier issue since if I do not set IO_REPARSE then the file is not reparsed as I am wanting. Is this just a verifier issue that I can work around somehow or will this be causing more problems that I have not yet uncovered?
I have certainly does this across volumes in PreCreate. Mount Points (which are just reparse points) also work across volumes. Like I said I've never tried to do this in PostCreate so not sure if there's going to be anything you need to wrestle with. In theory it should work, but "no plan survives contact with the enemy" so you'll need to do some nature study.
I'd suggest starting with SimRep and making it work to another volume. Then try to move it to PostCreate and see what happens.
No. It's not a directory attribute so you can't know until you open it. Most filters that reparse A to B do it in PreCreate using a policy based on the directory path. So, if it's in "X:\MyStuff" you reparse, anything else you let through. It doesn't necessarily have to work that way, that's just the common use case.
Yeah, not sure. It's unusual to do this in PostCreate so this could be a legitimate bug (did you also set IoStatus.Status to STATUS_REPARSE?) or a wonky FltMgr Verifier check. I'd compare the Callback Data, File Object, and underlying IRP in the case of hitting a symlink and what you're trying to do.
I am setting IoStatus.Status to STATUS_REPARSE and IoStatus.Information to IO_REPARSE. Without the IO_REPARSE in the IoStatus.Information field this does not seem to work but instead continues to try to reparse.
If I ignore the verify break this appears to work but I need a way to handle this and make verifier happy since I do not want to disable verifier. I also need to make sure this is not masking other problems that may come up later.
If this is unusual to handle reparse in post create how do I handle this in precreate. In pre create the FILE_OBJECT is not opened yet so I cannot get access to the reparse data. The examples I have seen so far it just looks like the reparse is determined by the file path. In my case I need to see the content of the file (or at least the reparse data) to make a decision. This appears to not be available in pre create.
By clearing the disposition and setting it to IO_REPARSE (0) which happens to match up to FILE_SUPERSEDE this seems to be working. Looking at Process Monitor it appears to be doing a reparse as I was wanting.
Yeah, I wouldn't just do that without understanding the FltMgr Verifier warning and why it's not happy. It's unclear if that Verifier error reflects an actual problem or if Verifier is just confused by what you're trying to do.
And in any case setting the disposition to IO_REPARSE doesn't really make sense as that's not a create disposition. The fact that it happens to share a value with one is either a happy or unhappy accident (depending on how you look at it )