首页 > 解决方案 > Windows 投影文件系统只读?

问题描述

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

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

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

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

标签: winapiprojfs

解决方案


简短的回答:

  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)

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

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

应用:

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

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

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


推荐阅读