锦方的个人网页 · 如果有一天你突然想起了我


在exFAT硬盘里移动文件,为什么有时仍然必须复制一遍

目录

最近我在测试Find Duplicate Files的文件清理流程时,遇到了一个看起来不太合理的问题。

明明源文件和目标目录都在同一块exFAT外置硬盘里,软件执行“移动”时,为什么还要重新复制文件?

我们平时在访达里移动同一块磁盘上的文件,通常很快。文件再大,也可能一下就完成。所以第一感觉会认为,软件只要修改一下路径就可以了,没有必要重新读写整个文件。

后来继续处理清理流程,我发现这里混在一起的是三件不同的事:普通文件移动、跨卷移动,以及带有防误删要求的安全移动。

它们在界面上都叫“移动”,底层做的事情并不一样。

先说结论

在同一个APFS卷内,文件通常可以通过重命名直接改变路径,不需要复制文件内容。

如果源目录和目标目录不在同一个卷,移动本质上只能是:

复制文件 → 确认目标完整 → 删除源文件

同一个exFAT卷内虽然也能进行普通重命名,但Find Duplicate Files需要的是一种“目标文件不能已经存在”的原子移动。

当前使用的严格移动方式在exFAT上可能不被支持。如果改用普通重命名,软件就失去了原子防覆盖保证。

因此在不能证明安全重命名成立时,我宁可让它复制一遍,也不让它直接冒险移动。

这并不代表所有exFAT文件移动都必须复制,而是清理工具采用了更严格的安全边界。

访达里的“移动”只是一个界面概念

用户看到的移动,是文件从一个目录出现在另一个目录。

文件系统可以用不同方式完成它。

如果源文件和目标目录在同一个卷,最快的方法通常只是修改目录记录。文件内容还在原来的存储位置,没有重新读写几GB甚至几十GB的数据。

如果跨越两个卷,例如从Mac内置硬盘移动到外置硬盘,就不能只修改目录记录。两个卷有各自独立的文件系统,必须先把数据写到目标卷,再删除源卷上的文件。

所以“都在同一块硬盘里”也不一定代表可以直接移动。一块硬盘可能被划分成多个卷,从一个卷移动到另一个卷,仍然属于跨卷操作。

APFS为什么可以直接移动

在同一个APFS卷里,macOS可以使用带有排他语义的原子重命名。

Find Duplicate Files使用这类能力时,会要求目标路径原本不存在。只要同名文件已经存在,操作就应该失败,而不是静默覆盖。

这个条件对清理工具非常重要。

例如软件准备把:

/资料/重复文件.zip

移动到:

/隔离区/重复文件.zip

如果隔离区里已经存在同名文件,软件不能猜测哪一个应该保留,也不能先删除目标文件再继续。它应该停止操作,把冲突显示给用户处理。

APFS上的原子排他重命名可以把“确认目标不存在”和“完成移动”作为一次文件系统操作。成功就是路径已经切换,失败时源文件仍然留在原处。

这种情况下没有必要复制文件内容,速度也会很快。

exFAT的问题不是不能重命名

exFAT当然支持普通的文件和目录移动。

真正的问题是,它不一定支持清理工具需要的那套原子排他语义。严格移动调用可能直接返回不支持,普通重命名虽然有机会成功,却不能提供完全相同的防覆盖保证。

如果为了速度直接退回普通重命名,就会产生新的问题:

这些情况不一定经常发生,但清理工具处理的是用户原始文件。不能因为概率低,就把后果交给用户承担。

所以我现在的策略是:能证明同卷原子移动安全时才直接移动;不能证明时,进入复制和校验流程。

安全复制不是复制完就删除

这里的复制也不是简单执行一次复制命令,然后马上删除源文件。

一个比较完整的过程应该是:

  1. 冻结这次确认过的文件路径和文件身份。
  2. 在目标位置排他创建暂存文件,禁止覆盖现有文件。
  3. 分块复制文件内容。
  4. 把事务状态写入恢复记录。
  5. 完整读回目标文件并重新计算SHA-256。
  6. 确认目标大小、内容和准备移动的源文件一致。
  7. 更新事务检查点。
  8. 最后才删除源文件。

任何一步失败,都不应该继续删除源文件。

例如复制完成后目标文件无法完整读回,说明“写入完成”并不能证明文件真的可以正常读取。即使路径、大小和修改时间看起来都对,也必须保留源文件并把这次操作标记为需要检查。

这也是为什么目标文件要完整读回一次。稳定的文件身份只能说明“还是这个文件”,不能证明写进去的每个字节都正确。

一次失败反而证明了安全策略有效

我在一次外置硬盘测试里遇到过暂存失败。

最终报告是30个文件失败,成功清理0个文件,释放空间0字节。

从结果看,这次操作完全没有完成,体验当然不好。但检查以后发现,失败发生在文件被排他暂存之前,没有进入复制和删除阶段,所有源文件仍然存在。

这正是我希望看到的失败方式。

文件工具不应该为了让报告看起来成功,就在安全条件不足时继续向后执行。失败可以重新处理,源文件被错误删除就不一定能恢复。

为什么这种移动会比较慢

安全复制至少会产生几次完整I/O:

文件很大、机械硬盘速度较低,或者同一块外置硬盘同时承担读取和写入时,等待时间会比较明显。

这部分可以优化,但不能通过取消最终校验来优化。

我更愿意做的是减少重复的无效读取、使用顺序分块读写,并限制同一块exFAT设备上的并发任务。这样可以减少随机寻道和I/O争用,同时继续保留目标文件完整读回、源文件重新校验和事务恢复记录。

性能优化应该删除重复工作,不能删除安全证据。

能不能让用户自己选择普通移动

技术上可以增加一个“更快但降低保护”的选项,在同卷exFAT上尝试普通重命名。

但这个选项不能悄悄成为默认值。

如果以后提供,我认为至少要明确告诉用户:

对于普通文件管理器,用户主动拖动一个文件,可以立即看到冲突并选择怎么处理。重复文件清理工具一次可能处理几百、几千个文件,更应该采用保守默认值。

我的结论

“移动文件”不一定只是修改路径。

同卷APFS、同卷exFAT和跨卷移动,底层能力和安全保证都不同。对普通使用来说,成功到达目标目录可能就够了;对清理工具来说,还要证明没有覆盖其他文件、目标内容完整、事务可以恢复,并且只有在这些条件全部成立后才能删除源文件。

所以在exFAT硬盘里看到软件重新复制文件,不一定是程序写得低效。它可能是在用更多时间换取一个很朴素的保证:如果移动失败,原文件还在。

我做Find Duplicate Files时会继续保留这条边界。可以慢一点,也可以让一次操作失败,但不能为了让进度条更快,就省掉删除源文件之前最关键的证明。

关于这个工具整体的防误删思路,可以继续看重复文件清理工具最危险的不是漏扫,而是误删

系列:技术

该系列自动来自分类: 技术

  1. 写一个macOS重复文件清理工具:为什么哈希不是难点,误删防御才是核心
  2. 在exFAT硬盘里移动文件,为什么有时仍然必须复制一遍 (当前)

下一篇推荐

系列继续阅读

写一个macOS重复文件清理工具:为什么哈希不是难点,误删防御才是核心