记录一个 cp 在 btrfs 文件系统上出现的奇怪的问题

这是一个非常奇怪的问题,原因尚不明确,仅记录一下备查。

使用 cp 命令,将一个位于 btrfs 上的 so 文件复制到其他地方时(无论目标文件系统为何),复制后的文件与源文件内容不一致,但大小相同。

文件系统情况:

1
2
3
/tmp    tmpfs
~       btrfs
/usr    btrfs

问题过程如下

从官网下载了 libonnxruntime.so 文件,放置在 ~/dl/libonnxruntime.so,此时代码直接加载这个文件,可以正常运行。

将文件复制到 /usr/local/lib

1
cp ~/dl/libonnxruntime.so /usr/local/lib

结果代码无法正常运行,用 sha256sum 检查发现文件内容不一致,但大小是相同的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
gs@gslj:~ % md5sum /usr/local/lib/libonnxruntime.so 
cc5c59484019c0a4b92fb3a0606ebffc  /usr/local/lib/libonnxruntime.so
gs@gslj:~ % sha256sum /usr/local/lib/libonnxruntime.so
cbdf3e64af08c3531e3f868d8374a87ec2fae1d98d44f3499b0b45318dd47aa7  /usr/local/lib/libonnxruntime.so
gs@gslj:~ % sha256sum ~/dl/libonnxruntime.so
a34d8c226994dd4af2b2250442fc236ebba97aefc7ec451c35a86216588d2f69  /home/gs/dl/libonnxruntime.so
gs@gslj:~ % ls -l /usr/local/lib/libonnxruntime.so 
-rwxrwxr-x 1 gs gs 15461048  3月27日 11:26 /usr/local/lib/libonnxruntime.so
gs@gslj:~ % ls -l ~/dl/libonnxruntime.so.1.17.1 
-rwxrwxr-x 1 gs gs 15461048  2月24日 08:43 /home/gs/dl/libonnxruntime.so

使用 hexdump 检查文件头,发现复制后的文件缺少 ELF 头(开头一大部分内容是全 0):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
gs@gslj:~ % hexdump -C /usr/local/lib/libonnxruntime.so | head
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00020000  50 00 ea 00 00 00 00 00  08 00 00 00 00 00 00 00  |P...............|
00020010  3a 7a 08 00 00 00 00 00  58 00 ea 00 00 00 00 00  |:z......X.......|
00020020  08 00 00 00 00 00 00 00  50 5c 26 00 00 00 00 00  |........P\&.....|
00020030  60 00 ea 00 00 00 00 00  08 00 00 00 00 00 00 00  |`...............|
00020040  20 5f 26 00 00 00 00 00  68 00 ea 00 00 00 00 00  | _&.....h.......|
00020050  08 00 00 00 00 00 00 00  70 5c 26 00 00 00 00 00  |........p\&.....|
00020060  78 00 ea 00 00 00 00 00  08 00 00 00 00 00 00 00  |x...............|
00020070  38 32 eb 00 00 00 00 00  80 00 ea 00 00 00 00 00  |82..............|
gs@gslj:~ % hexdump -C ~/dl/libonnxruntime.so | head   
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  03 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  40 00 00 00 00 00 00 00  38 e3 eb 00 00 00 00 00  |@.......8.......|
00000030  00 00 00 00 40 00 38 00  0a 00 40 00 1e 00 1d 00  |[email protected]...@.....|
00000040  01 00 00 00 04 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  08 de 06 00 00 00 00 00  08 de 06 00 00 00 00 00  |................|
00000070  00 10 00 00 00 00 00 00  01 00 00 00 05 00 00 00  |................|
00000080  00 e0 06 00 00 00 00 00  00 e0 06 00 00 00 00 00  |................|
00000090  00 e0 06 00 00 00 00 00  b5 bd b9 00 00 00 00 00  |................|

使用 rsync 复制文件,完全没有问题,可以确定该问题与 cp 有关联。但具体原因是什么尚不清楚。实际上,这个问题还于 btrfs 有关联。

使用 rsync 将文件复制到 /tmp 后,再使用 cp 命令将 /tmp/libonnxruntime.so 复制到任意位置,则结果完全正确。

查看 dmesg 未发现任何异常情况。

问题触发条件总结

  1. 使用 cp 复制文件
  2. 源文件必须位于 btrfs 之上

附录

更具体的文件系统情况

1
2
3
4
5
/dev/mapper/gslj--vg-root on / type btrfs (rw,relatime,compress=zstd:3,ssd,space_cache,subvolid=5,subvol=/)
/dev/mapper/gslj--vg-home on /home type btrfs (rw,relatime,compress=zstd:3,ssd,space_cache,subvolid=5,subvol=/)
/dev/mapper/gslj--vg-root on /data/btrfs type btrfs (rw,relatime,compress=zstd:3,ssd,space_cache,subvolid=5,subvol=/)
/dev/sda on /data type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=4928480k,nr_inodes=1232120,mode=700,uid=1000,gid=1000,inode64)

系统信息

1
2
$ uname -a
Linux gslj 6.6.15-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.6.15-2 (2024-02-04) x86_64 GNU/Linux

最后附上与 Claude 的对话

来自用户的消息

我遇到一个很奇怪的问题 我用 cp 命令复制一个动态库

1
 sudo cp dl/li bo nn xr u nt i me /usr/local/lib/libonnxruntime.so

复制完成之后,用 ls 查看源文件和目标文件的大小,文件大小相同 然而,当我用 md5sum 对比两个文件时,却发现两个文件不相同 实际上,我在代码里面加载 /usr/local/lib/ 下面的动态库,也无法重新加载,说明 cp 复制过去的文件是有问题的

奇怪的点在于:

  1. cp 复制过程没有报任何错误
  2. 如果我用 rsync 进行复制,则可以正确复制

这是 cp 有什么特殊机制吗?

来自 Ai 的消息

你遇到的问题确实很奇怪。通常情况下,使用 cp 命令复制文件不会出现文件内容不一致的问题。cp 命令会按字节复制源文件的内容到目标文件,并且在复制完成后,源文件和目标文件的大小和内容应该是完全相同的。

但是,有几个可能的原因导致你遇到这个问题:

  1. 文件系统问题:如果目标文件所在的文件系统存在问题或者处于不一致的状态,可能会导致复制的文件内容出现异常。你可以尝试运行文件系统检查工具(如 fsck)来检查和修复文件系统。

  2. 硬件问题:如果硬件(如内存或硬盘)存在问题,可能会导致文件复制过程中出现数据损坏。你可以运行内存测试工具和硬盘检测工具来排查硬件问题。

  3. 文件正在被其他进程使用:如果在复制文件的同时,有其他进程正在读取或写入该文件,可能会导致复制的文件内容不一致。确保在复制文件时,没有其他进程正在使用该文件。

  4. 文件权限问题:确保你有足够的权限读取源文件和写入目标文件。虽然你使用了 sudo 命令,但还是建议仔细检查文件的权限设置。

建议你尝试以下几个步骤来排查问题:

  1. 使用 md5sumsha256sum 等工具计算源文件的校验和,确保源文件没有问题。

  2. 尝试将文件复制到其他目录或另一个文件系统中,看是否能够正确复制。

  3. 使用 rsync 命令复制文件,并检查复制后的文件是否与源文件一致。

  4. 检查文件系统和硬件是否存在问题,必要时进行修复。

如果问题仍然无法解决,你可以考虑在相关的技术论坛或社区中寻求进一步的帮助和建议。

来自用户的消息

我进一步分析了一下,原始的文件头有 ELF 标识,然而 cp 复制过去的文件开头是全 0x00,没有任何 ELF 文件头标识

来自 Ai 的消息

根据你的进一步分析,原始文件是一个 ELF 格式的动态库文件,但是使用 cp 命令复制后的文件开头全部为 0x00,没有 ELF 文件头标识。这说明复制过程中确实出现了问题,导致复制后的文件内容异常。

以下是一些可能的原因和建议:

  1. 文件系统权限问题:确保你对目标目录 /usr/local/lib/ 有写入权限。尽管你使用了 sudo 命令,但还是建议仔细检查目标目录的权限设置。你可以使用 ls -ld /usr/local/lib/ 命令查看目录的权限。

  2. 文件系统空间不足:确保目标文件系统有足够的空间来存储复制的文件。你可以使用 df -h 命令查看文件系统的空间使用情况。

  3. 尝试使用其他复制方式:

    • 使用 rsync 命令复制文件,如 sudo rsync -avP dl/libonnxruntime.so /usr/local/lib/libonnxruntime.so
    • 使用 dd 命令复制文件,如 sudo dd if=dl/libonnxruntime.so of=/usr/local/lib/libonnxruntime.so bs=4M
  4. 检查源文件的完整性:确保源文件 dl/libonnxruntime.so 是一个完整且有效的 ELF 动态库文件。你可以使用 file 命令查看文件的类型和格式,如 file dl/libonnxruntime.so

  5. 尝试复制到其他目录:将文件复制到其他目录,如你的家目录或临时目录,看是否可以成功复制。这有助于确定问题是否与目标目录有关。

  6. 检查系统日志:查看系统日志文件(如 /var/log/syslog/var/log/messages)是否有任何与文件复制相关的错误信息。

如果以上建议都无法解决问题,你可能需要进一步分析文件系统和硬件是否存在问题。你可以尝试使用文件系统检查工具(如 fsck)来检查和修复文件系统,或者使用内存测试和硬盘检测工具来排查硬件问题。

如果问题仍然存在,建议你在相关的技术社区或论坛中寻求更多的帮助和建议,提供更详细的信息,如系统版本、文件系统类型、硬件配置等,以便获得更针对性的解决方案。

来自用户的消息

经过进一步测试,我发现只要使用 cp 复制这个文件,无论复制到何处,文件内容都会变化,而变化后的文件内容都是一致的 而使用其他工具进行复制,则完全没有问题 这到底是为什么呢?我倾向于是 cp 引起的问题

来自 Ai 的消息

根据你的进一步测试,无论将文件复制到何处,使用 cp 命令复制后的文件内容都会发生变化,而变化后的文件内容是一致的。这确实表明问题可能与 cp 命令本身有关。

以下是一些可能的原因和解释:

  1. 文件系统缓存问题:cp 命令可能会受到文件系统缓存的影响。如果文件系统缓存出现问题或者没有正确同步,可能会导致复制的文件内容异常。你可以尝试在复制文件之前执行 sync 命令,强制将文件系统缓存刷新到磁盘。

  2. cp 命令的别名或包装器:检查是否有为 cp 命令定义了别名或者使用了包装器脚本。这些别名或包装器可能会修改 cp 命令的行为,导致复制的文件内容发生变化。你可以使用 type cp 命令查看 cp 命令的实际定义。

  3. cp 命令的版本或实现问题:不同版本或实现的 cp 命令可能存在差异或bug。你可以尝试使用其他版本的 cp 命令或者使用其他复制工具,如 rsyncdd,看是否能够正确复制文件。

  4. 文件系统或存储设备的问题:虽然其他复制工具能够正常工作,但文件系统或存储设备本身可能存在一些特定的问题,导致 cp 命令在复制该文件时出现异常。你可以尝试在其他文件系统或存储设备上进行复制测试,看是否能够排除这种可能性。

为了进一步排查问题,你可以尝试以下几个步骤:

  1. 使用 strace 命令跟踪 cp 命令的系统调用,看是否能够发现任何异常行为,如 strace -f -o cp_trace.log cp dl/libonnxruntime.so /usr/local/lib/libonnxruntime.so

  2. 尝试使用 cp 命令复制其他类型的文件,如文本文件或二进制文件,看是否也会出现类似的问题。

  3. 在其他系统或环境中尝试复制该文件,看问题是否能够重现。

  4. 检查系统日志和 dmesg 输出,看是否有任何与文件复制相关的错误信息。

如果问题仍然无法确定原因,建议你在相关的技术社区或论坛中寻求更多的帮助和建议,提供更详细的信息,如系统版本、cp 命令的版本、文件系统类型、存储设备信息等,以便获得更专业的分析和解决方案。

来自用户的消息

我使用 –debug 查看 cp 的具体行为,输出如下,你能看出什么端倪吗?

1
2
3
cp --debug dl/libonnxruntime.so dl/biu
'dl/libonnxruntime.so' -> 'dl/biu'
copy offload: unknown, reflink: yes, sparse detection: unknown

来自 Ai 的消息

根据你提供的 cp 命令的调试输出,可以看到一些有趣的信息:

1
copy offload: unknown, reflink: yes, sparse detection: unknown

这行输出提供了一些关于 cp 命令复制行为的线索:

  1. copy offload: unknown:这表示 cp 命令无法确定是否可以使用复制卸载(copy offload)功能。复制卸载是一种优化技术,可以将复制操作卸载到存储设备上,以提高复制性能。但是,如果存储设备不支持复制卸载或者出现问题,可能会导致复制的文件内容异常。

  2. reflink: yes:这表示 cp 命令尝试使用引用链接(reflink)来执行复制操作。引用链接是一种高效的复制方式,它允许多个文件共享相同的数据块,而不是实际复制数据。然而,如果文件系统或存储设备不支持引用链接,或者引用链接的实现存在问题,可能会导致复制的文件内容出现异常。

  3. sparse detection: unknown:这表示 cp 命令无法确定是否需要执行稀疏文件检测。稀疏文件是指文件中包含大量空字节(零字节)的文件。cp 命令可以通过检测和优化稀疏文件的复制来提高效率。但是,如果稀疏文件检测出现问题,可能会影响复制的文件内容。

根据这些信息,可以推测可能是 cp 命令在复制过程中使用了某些优化技术,如复制卸载、引用链接或稀疏文件检测,而这些优化技术可能与你的文件系统或存储设备不兼容,或者存在实现上的问题,导致复制的文件内容异常。

为了进一步排查问题,你可以尝试以下几个方法:

  1. 使用 cp 命令的 --reflink=never 选项禁用引用链接,看是否能够解决问题,如 cp --reflink=never dl/libonnxruntime.so dl/biu

  2. 使用 cp 命令的 --sparse=never 选项禁用稀疏文件检测,看是否能够解决问题,如 cp --sparse=never dl/libonnxruntime.so dl/biu

  3. 尝试在其他文件系统或存储设备上执行复制操作,看问题是否仍然存在。

  4. 检查文件系统和存储设备的健康状况和兼容性,确保它们能够正常工作并支持所需的功能。

如果问题仍然无法解决,建议你在相关的技术社区或论坛中寻求更多的帮助和建议,提供更详细的信息,如文件系统类型、存储设备型号、内核版本等,以便获得更专业的分析和解决方案。

来自用户的消息

我尝试了 1 和 2,但问题依旧存在 接着我尝试了 3: 首先使用 rsync 将这个文件复制到 /tmp (tmpfs 文件系统)上,保证这个文件是正确的 接着,使用 cp 命令在 /tmp 上执行复制操作,原路径和目标路径都是 /tmp,可以发现,复制正常了 为进一步确认问题,使用 cp 命令将 /tmp 上的文件复制到 /usr/local/lib 下,发现这次复制结果是正确的

那么问题触发条件为:

  1. 使用 cp 复制
  2. 源文件保存在 btrfs 分区上

来自 Ai 的消息

根据你的进一步测试,问题的触发条件确实与文件系统有关。当源文件位于 Btrfs 文件系统上时,使用 cp 命令复制会导致文件内容异常。而当源文件位于 tmpfs 文件系统(内存文件系统)上时,使用 cp 命令复制则能够正常工作。

这表明问题可能与 Btrfs 文件系统的特性或实现有关。Btrfs 是一种复杂的文件系统,它提供了许多高级功能,如写时复制(COW)、快照、压缩等。这些功能可能会与 cp 命令的某些优化技术产生冲突或兼容性问题,导致复制的文件内容出现异常。

以下是一些可能的原因和解释:

  1. Btrfs 的写时复制(COW)机制:Btrfs 使用写时复制来优化写入操作,当文件被修改时,它会创建一个新的副本,而不是直接覆盖原始数据。这种机制可能会与 cp 命令的某些优化技术(如引用链接)产生冲突,导致复制的文件内容异常。

  2. Btrfs 的压缩功能:Btrfs 支持透明压缩,可以自动压缩存储在文件系统上的数据。如果源文件是压缩的,而 cp 命令在复制时没有正确处理压缩属性,可能会导致复制的文件内容异常。

  3. Btrfs 的元数据管理:Btrfs 使用复杂的元数据结构来管理文件和目录,如 B 树和扩展属性。如果 cp 命令在复制过程中没有正确处理这些元数据,可能会导致复制的文件内容异常。

  4. Btrfs 的缓存和同步机制:Btrfs 有自己的缓存和同步机制,用于优化文件系统的性能。如果 cp 命令在复制过程中没有正确与这些机制配合,可能会导致复制的文件内容异常。

为了解决这个问题,你可以尝试以下几个方法:

  1. 使用其他复制工具,如 rsynctar,来复制文件,看是否能够避免问题。

  2. 在复制文件之前,先将源文件从 Btrfs 文件系统复制到其他文件系统(如 tmpfs 或 ext4),然后再使用 cp 命令进行复制。

  3. 检查 Btrfs 文件系统的健康状况和一致性,使用 btrfs scrub 命令扫描文件系统,并使用 btrfs check 命令检查文件系统的完整性。

  4. 考虑在 Btrfs 文件系统上禁用某些高级功能,如压缩或写时复制,看是否能够缓解问题。

如果问题仍然无法解决,建议你在 Btrfs 相关的社区或论坛中寻求更多的帮助和建议,提供更详细的信息,如 Btrfs 的版本、内核版本、挂载选项等,以便获得更专业的分析和解决方案。