我在使用 rsync 时遇到了一个奇怪的问题(没有-c
,因此涉及 rsync 的“快速检查”算法)将已同步的文件一遍又一遍地复制到 exFAT 格式的驱动器。
所涉及的源文件存储在 HFS 卷上,是通过从.docx
Word 文档中自动提取图像的过程创建的,修改时间为 1980 年 1 月 1 日 00:00:00,这也恰好是 exFAT 时间戳格式支持的最小时间。
之后,rsync -a
exFAT 上的这些目标文件的修改时间为 1980 年 1 月 1 日 02:00:00。
看起来我的 OS X exFAT 驱动程序支持的最短时间差了两个小时。
这就是导致 rsync 的快速检查算法认为文件不同的原因。
下面的测试似乎证实了这一点:
# create a 10Mb exFAT disk image:
hdiutil create -size 10m -fs ExFAT -volname EXFATTEST exfattest.dmg
# attach the diskimage
hdiutil attach exfattest.dmg
# create a test file with a creation time of Jan 1, 1980 00:00:00
touch -t "198001010000.00" /Volumes/EXFATTEST/test1.txt
ls -lT /Volumes/EXFATTEST/test1.txt
-rwxrwxrwx 1 user staff 0 Jan 1 02:00:00 1980 /Volumes/EXFATTEST/test1.txt
# ^^ off by two hours
# try a file with 3am
touch -t "198001010300.00" /Volumes/EXFATTEST/test2.txt
ls -lT /Volumes/EXFATTEST/test2.txt
-rwxrwxrwx 1 user staff 0 Jan 1 03:00:00 1980 /Volumes/EXFATTEST/test2.txt
# ^^ works as expected
# unmount the disk image
hdiutil detach /Volumes/EXFATTEST/
在 Linux 上进行等效测试不会显示此错误:
truncate -s 10M exfattest.img
mkfs.exfat exfattest.img
mkdir /mnt/EXFATTEST/
mount -o loop exfattest.img /mnt/EXFATTEST/
touch -t "198001010000.00" /mnt/EXFATTEST/test.txt
ls --full-time /mnt/EXFATTEST/test.txt
rwxr-xr-x 1 root root 0 1980-01-01 00:00:00.000000000 +0100 /mnt/EXFATTEST/test.txt
# ^^ correct modification time
umount /mnt/EXFATTEST/
我的简单解决方案是使用 更新源文件的创建时间touch
。
但是,我想知道这是否真的是一个错误,以及是否有人可以在较新的 OS X 版本上重现此错误,因为 Apple 不允许升级我仍然运行良好的 2008 年初 iMac ;)
我仍在使用 El Capitan:
sw_vers
ProductName: Mac OS X
ProductVersion: 10.11.6
BuildVersion: 15G22010
更新:
检查完 exFAT 表后,似乎我的 El Capitan exFAT 驱动程序总是用于0xfc
数据LastModifiedTimezoneOffset
字段。
这个有符号字节值以 15 分钟为增量指定了与 UTC 的偏移量。0xfc
以-4
十进制表示,将被解释为一个小时的负 UTC 偏移量。
我不知道为什么驱动程序会这样做,但如果它使用静态时区偏移量 -1,它也会总是存储时间戳时必须从相应的 UTC 时间戳中减去 1 小时,并从磁盘读回时间戳时将存储的值加上 1 小时。
这基本上是可行的,除非 UTC 时间戳早于 1980 年 1 月 1 日 01:00,因为 exFAT 上文件支持的最小时间戳是 1980 年 1 月 1 日 00:00。
所以我认为这是一个错误,因为 El Capitan 的 exFAT 驱动程序无法设置 exFAT 支持的最小文件修改时间戳。
以下测试似乎证实了这一点。它使用TZ
环境变量强制使用 UTC 时区,而不受系统时区的影响:
# set the minimum possible exFAT file modifification timestamp in UTC
TZ=UTC touch -t '198001010000.00' /Volumes/EXFATTEST/test.txt
# read back the stored timestamp in UTC
TZ=UTC ls -lT /Volumes/EXFATTEST/test.txt
-rwxrwxrwx 1 gollum staff 0 Jan 1 01:00:00 1980 /Volumes/EXFATTEST/test.txt
# ^^^^^^^^ ERROR: off by one hour
在 HFS 上创建此日期的文件并将其复制到 exFAT 卷时出现同样的错误:
# set Jan 1, 1980 00:00:00 on HFS (e.g. file in home directory)
TZ=UTC touch -t '198001010000.00' $HOME/test2.txt
TZ=UTC ls -lT $HOME/test2.txt
-rw-r--r-- 1 gollum staff 0 Jan 1 00:00:00 1980 /Users/gollum/test2.txt
# ^^^^^^^^ CORRECT!
# copy the file to the exFAT volume and check the destination date
cp -a $HOME/test2.txt /Volumes/EXFATTEST/test2.txt
TZ=UTC ls -lT /Volumes/EXFATTEST/test2.txt
-rwxrwxrwx 1 gollum staff 0 Jan 1 01:00:00 1980 /Volumes/EXFATTEST/test2.txt
# ^^^^^^^^ ERROR: off by one hour
我做过的另一个有趣的测试是获取在 Linux 上创建的 exFAT 磁盘映像(其中包含具有正确时间戳的测试文件)并将其安装在 OS X 上:
TZ=UTC ls -lT
total 0
-rwxrwxrwx 1 gollum staff 0 Jan 1 00:00:00 1980 test.txt
# ^^^^^^^^ correct!
但是,复制此文件时,日期又偏离了一个小时。因此,即使文件使用非时区偏移存储,OS X exFAT 驱动程序至少能够正确读取时间戳0xfc
。太棒了!
现在反过来:在 Linux 上安装 OS X dmg 会显示 OS X exFAT 驱动程序的第二个错误:
mount -o loop exfattest.dmg /mnt/EXFATTEST/
FUSE exfat 1.0.1
ERROR: bad date 1980-01-00
# ^^^ OS X stored the DAY as zero ::facepalm::
这是开发过程中缺少极端情况测试的另一个完美例子。
这基本上就是我的问题的答案。但是,如果此错误也发生在比 El Capitan OS X 版本更新的版本上,我会接受一个能证明我的发现是错误的或证实我的发现并能回答我问题的答案。
更新 2:
我看到了亚当·哈里森(英国数字取证调查员)的一篇博客文章:与不同操作系统相关的 exFAT 时间戳行为
引用:
Ubuntu 16.04 和 Ubuntu 18.04
所有时区均以 UTC 记录。时区字段始终设置为 00,表示未使用。OSX 10.13.3
时区字段 - 始终设置为“FC”,即 UTC-1,我不知道为什么......
所以这似乎与我的发现相符。
答案1
exFAT 实现可以选择存储 UTC 时间戳,而不是本地时间戳。(大多数现代文件系统都只使用 UTC 时间戳,但 exFAT 可能希望保留与原始 FAT 的一些兼容性。)
macOS exFAT 驱动程序可能被编写为使用 UTC 时间戳模式 - 这意味着当您在中指定 1980-01-01 00:00.00(本地)时touch
,您实际上是在要求驱动程序将 1979-12-31 23:00.00(UTC)存储在磁盘上。
Linuxexfat 实用程序同时,包使用本地时间并存储重新应用系统时区偏移的时间戳,因此本地 00:00 也存储为 00:00。