microsoft / ProjFS-Managed-API

A managed-code API for the Windows Projected File System
Other
142 stars 34 forks source link

Is there a way to clean up ProjFS placeholders/markup without deleting everything? #53

Closed FeralSquid closed 3 years ago

FeralSquid commented 3 years ago

Hello,

I need to support a user converting a directory back to not being virtual. Ideally this would go back to letting them manipulate the contents under that directory like normal (creating new files, etc).

The main reason for wanting this, versus just telling them to delete the virtual root, is that we want to preserve any local (Full) files they have under the root, and enable them to then work under that same path like normal.

I can write a utility to, after virtualization is disabled, go through an delete any non-Full files, that is easy.

Directories are the tricky bit.

I have found that I can convert a hydrated placeholder directory back to a normal looking/acting directory with DeviceIoCtrl(FSCTL_DELETE_REPARSE_POINT).

If it is just a NON-hydrated placeholder, though, this mostly works but leaves the RECALL_ON_DATA_ACCESS attribute, which I can't seem to clear (I see a note on MSDN about only being able to set this from kernel mode, which is presumably why).

That does allow me to create files in the directory once again, so seems like it gives me pretty much what I want...

So, I'm wondering:

  1. Is this method of de-virtualizing directories using FSCTL_DELETE_REPARSE_POINT okay to do (not leaving anything in a bad state, etc)?
  2. Is there a better way?
  3. Is there a way to get rid of the RECALL_ON_DATA_ACCESS attribute, and/or does it matter?

Thanks!

cgallred commented 3 years ago

I don't think I understand what your scenario is. Nothing about ProjFS should prevent users from manipulating contents under a placeholder directory. They should already be able to create new files, etc. Is this only when the provider has been stopped?

Deleting the reparse points on files/directories under a virtualization root disconnects them from the backing store. They are effectively "full" files/directories at that point (see "Full file/directory" on this page).

There isn't a way to turn off RECALL_ON_DATA_ACCESS from user mode. The reason for that is so that anti-malware filters can trust the bit and be confident that it is okay to skip reading the contents of a file that is marked with it, thereby avoiding unwanted hydration. For directories RECALL_ON_DATA_ACCESS tells them that they can safely enumerate for local-only entries (FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY) to avoid the extra expense of getting virtual-only directory entries. RECALL_ON_DATA_ACCESS means basically the same thing as the OFFLINE attribute, but the OFFLINE attribute can be manipulated from user-mode. That makes it so anti-malware filters can't trust that it is safe to skip scanning the file.

Since RECALL_ON_DATA_ACCESS on a directory is just a hint on how to efficiently enumerated it, it doesn't matter whether it is set on a directory if there are no virtual items in it. A local-only and non-local-only enumeration on a directory with no virtual items gives identical results and performance. For directories ProjFS leaves that bit set always and never removes its reparse point. I.e. it never converts a directory from hydrated placeholder to full.

FeralSquid commented 3 years ago

Right, my scenario is that a user was using a a directory as virtual for some time, then decides they want to disable it and go back to non-virtual ... so we want to allow them to create files, etc again ideally without losing the full files/etc that had under the virtual root.

It sounds like I can go with the FSCTL_DELETE_REPARSE_POINT method, so I think I'm good. I just wanted to make sure that wasn't leaving something in a bad state.

Thanks!