Windows 投影文件系统只读?

Mic*_*kis 5 winapi projfs

我尝试使用Projected File System来实现用户模式 ​​ram 驱动器(以前我使用过 Dokan)。我有两个问题:

  1. 这是只读投影吗?从记事本打开文件并写入文件时,我找不到任何发送给我的通知。

  2. 使用 PrjWriteFileData() 后,文件是否实际创建在磁盘上?据我了解,是的。

在这种情况下,如果没有写入投影文件,那么人们可以用这个库做些什么有用的事情?在我看来,唯一有用的事情是最初从其他地方(比如远程仓库)创建一个目录树,但除此之外别无他法。多坎似乎仍然是要走的路。

Pav*_*hin 5

简短的回答:

  1. 不是只读的,但您无法通过投影文件系统将文件直接写入“源”文件系统。
  2. WriteFileData方法用于在“临时”(投影)文件系统上填充占位符文件,因此,它不会影响“源”文件系统。

长答案:

正如@zett42 的评论中所述,ProjFS 主要被设计为远程 git 文件系统。因此,任何文件版本控制系统的主要目标都是处理文件的多个版本。由此产生了一个问题 - 我们是否需要在 ProjFS 文件写入时覆盖远程存储库内的文件?这将是灾难性的。使用 git 时,您总是在本地写入文件,并且在将更改推送到远程存储库之前它们不会同步。

当您枚举文件时,没有任何内容写入本地文件系统。来自 ProjFS 文档:

当提供商首次创建虚拟化根时,它在本地系统上是空的。也就是说,后备数据存储中的任何项目尚未缓存到磁盘。

仅在文件打开后,ProjFS 才会在本地文件系统中为其创建一个“占位符” - 我假设它是一个具有特殊结构的文件(不是真正的文件)。

当打开虚拟化根目录下的文件和目录时,提供程序会在磁盘上创建占位符,并且当读取文件时,占位符会与内容混合。

“水合”是什么意思?最有可能的是,它代表了一种部分填充了真实数据的特殊数据结构。我将占位符想象成一块部分填充了数据的海绵。

打开项目时,ProjFS 会向提供者请求信息,以允许在本地文件系统中创建这些项目的占位符。当访问项目内容时,ProjFS 会向提供者请求这些内容。结果是,从用户的角度来看,虚拟化文件和目录看起来与已驻留在本地文件系统上的普通文件和目录类似。

仅在文件更新(修改)后。它不再是占位符 - 它变成“完整文件/目录”:

对于文件:文件的内容(主数据流)已被修改。该文件不再是其状态在提供者存储中的缓存。在本地文件系统上创建的文件(即根本不存在于提供商的存储中)也被视为完整文件。

对于目录:在本地文件系统上创建的目录(即根本不存在于提供程序的存储中)被视为完整目录。在磁盘上作为占位符创建的目录永远不会成为完整目录。

这意味着在第一次写入时,占位符将被本地 FS 中的真实文件替换。但是如何使“远程”文件与修改后的文件保持同步呢?(1)

当提供程序调用 PrjWritePlaceholderInfo 写入占位符信息时,它会在 placeholderInfo 参数的 VersionInfo 成员中提供 ContentID。然后,提供程序应记录在此视图中创建该文件或目录的占位符。

注意“提供者应该记录该文件的占位符”。这意味着为了稍后将文件与正确的视图表示同步,我们必须记住修改后的文件与哪个版本关联。想象一下我们在一个 git 存储库中并且我们更改了分支。在这种情况下,我们可能会在不同的分支中多次更新一个文件。现在,提供商为何以及何时致电PrjWritePlaceholderInfo

...这些占位符代表后备存储在创建时的状态。这些缓存的项目与提供者在枚举中投影的项目相结合,构成了客户端对后备存储的“视图”。有时,提供者可能希望更新客户端的视图,无论是因为后备存储中的更改,还是因为用户为更改其视图而采取的显式操作。

再次想象一下在 git 存储库中切换分支;如果另一个分支中的文件不同,则必须更新该文件。继续回答问题(1)。想象一下您想要从特定分支进行“推送”。首先,你要知道哪些文件被修改了。如果您在修改文件时没有记录占位符信息,您将无法正确完成操作(至少对于 git 存储库示例)。

还记得吗,占位符在修改时会被替换为真实文件?ProjFS 有OnNotifyFileHandleClosedFileModifiedOrDeleted事件。这是回调的签名:

public void NotifyFileHandleClosedFileModifiedOrDeletedCallback(
    string relativePath,
    bool isDirectory,
    bool isFileModified,
    bool isFileDeleted,
    uint triggeringProcessId,
    string triggeringProcessImageFileName)
Run Code Online (Sandbox Code Playgroud)

为了我们的理解,这里最重要的参数是relativePath。它将包含“临时”文件系统(预计)内已修改文件的名称。在这里你还知道该文件是一个真实的文件(不是占位符)并且它被写入磁盘(也就是说你将无法在文件写入之前拦截调用)。现在您可以将其复制到所需的位置(或稍后执行) - 这取决于您的目标。

回答问题#2,它似乎PrjWriteFileData仅用于填充“临时”文件系统,并且不能使用它来更新“源”文件系统。

应用:

对于应用程序,您仍然可以实现远程文件系统(而不是使用 Dokan),但所有写入都将缓存在本地,而不是直接写入远程位置。一些用例想法:

  1. 分布式文件系统
  2. 在线驱动客户端
  3. 文件系统“调度程序”(例如,您可以根据特定条件将文件写入不同的文件夹中)
  4. 文件版本控制系统(例如,您可以在修改后保留同一文件的不同版本)
  5. 将数据从您的应用程序镜像到文件系统(例如,您可以将带有缩进的文本文件“投影”到文件夹、子文件夹和文件)

PS:我不知道有任何未记录的 API,但从我的角度来看(根据文档),我们不能将 ProjFS 用于 ramdisk 等用途,也不能将文件直接写入“源”文件系统,而不将它们写入“首先是本地”文件系统。