是否有标准的文件保存和交换模式?

Is there a standard file save and swap pattern?

我们的产品对每个打开的文档文件都持有一个写独占的文件句柄,以确保我们对该文件拥有独占的写控制权。

因此,Windows 将不允许任何其他进程执行除从文件中读取以外的操作,也不能从资源管理器或其他进程中删除文件 - 因为打开(和写入独占)处理它。

但是,我们遇到了文件内容损坏的非常奇怪的边缘情况。我认为这与错误有关,或者可能是我对 Windows APIs 所保证的内容的误解 - 即为了在先前版本的顶部保存设计文件 - 我们目前持有文件句柄 - 我必须将句柄倒回文件的开头,将其写出,然后强制它刷新并在新位置截断(以防文件缩小 - 我们不希望文件末尾有额外的污泥- 这也是一种腐败形式)。在会话期间多次执行此操作 - 每次用户编辑然后保存他们的更改...

但是,有时我们的客户会报告所有这些导致文件损坏(仅通过网络 - 从不在本地)。

我们认为这可能是因为我们的实际保存过程稍微复杂一些:

  1. 回退(已经打开的)文件句柄
  2. 写出核心数据
  3. 刷新并截断句柄
  4. fseek 到文件末尾
  5. 写出缩略图数据(本质上是附加缩略图)
  6. 刷新并截断句柄

这可能只是 "don't flush, seek, write, flush" 的一个例子 - 这在 MS 的网络文件系统代码中引入了细微的错误(或者依赖于系统内置的不确定性 - 并且不能可靠地依赖)?

所以,我正在实施两层修复:

  1. 做一次倒带,然后写入核心数据+图像数据+刷新和截断(一次)
  2. 进行临时保存、关闭、重命名

没有。 2 有一些不错的功能 - 例如 "if there is a problem writing out the new file, the old one remains untouched." 这意味着在最坏的情况下他们的新数据不会被保存,但旧数据不会丢失。

这是"build a new copy then swap it into the real / active data structure."

经典模式的基本用法

很好 - 但我不知道如何 "swap contents of files"?

古典我会:

  1. 写满T(temp)并关闭
  2. 将一个(实际)文件重命名为 A.bak。
  3. 将 T 重命名为 A

(当然,我需要先删除之前的任何 A.bak)。

这很好 - 但同样 - 我们通常在 A 上有一个锁定句柄。所以这扩展到有点不完美:

  1. 写T
  2. 关闭我们对 A 的句柄
  3. 将 A 重命名为 A.bak
  4. 将 T 重命名为 A
  5. 获取 A 上的写独占句柄

我不喜欢这个的是 "too many moving parts."

您认为这不会发生 - 但文件系统索引或防病毒或备份软件都可能成为阻碍,而且非常 - 非常 - 经常发生(根据我们的经验)。

所以-理想情况下,我不想在任何时候放弃对A的控制!我想确保每次交接都不会受到防病毒软件或其他软件的影响,无法进入并进行改进。

理想情况下,事实上,我会:

  1. 写T
  2. 交换 T 和 A 的内容(要求文件系统实际 link 将 A 命名为 T 的内容)
  3. 永远幸福地生活下去...

那么,有没有其他人发现的交换 T 和 A 的模式?

是否有一组 API 调用来使这个更好/更健壮?

其他可能有助于重新思考我的方法的想法?

注意:MS 已弃用事务文件系统 API。所以这听起来像是一个非启动器 - 更不用说它在 Windows 下的所有文件系统上都不可用。

更新: FWIW,我将其实现为写临时文件,重命名原始文件,重命名临时文件,删除原始文件(加上必要的解锁并获得新文件lock) 使用 RAII 和 ScopeGuard 来处理任何故障回滚,当然回滚 - 是副作用和 OS 相关的,是 "best case scenarios" 并且不像 C++ 语言契约那样得到很好的保证他们自己。尽管如此,在测试过程中它还是非常有效的——从来没有给我一个错误的文件(而且我有意和无意地创建了一些问题,这些问题创建了一个错误的临时文件或在调用展开过程的算法期间以其他方式出错(抛出异常))。

更新二:"Final"算法为
1.(保存到临时本地验证副本)
2.保存到临时新文件
3.(验证新存档和验证匹配)
4. 锁定真实文件
5. 将真实文件重命名为临时旧文件并用临时文件替换原始文件(这包括传输属性、ACL 和时间戳 - 请参阅 ReplaceFile())
6. 获取我们的锁(如果被锁)
7. 成功(放弃我们的守卫)

ReplaceFile 是一个相当标准的方法。

它没有解决您的保持锁定问题,但它是您实施的测试版本,具有其他功能,例如将任何 ACL 从旧文件转移到新文件。

如果您保留自制程序实施,请确保您处理 Windows 上的文件删除并不总是立即的情况。例如,如果在将当前文件重命名为备份文件之前必须删除旧 "backup" 文件,则重命名可能会失败。