我如何在GNU Tar中发现错误

由多伦多大学Unix系统管理员Chris Siebenmann发布

有时,我的工作中会发生一些奇怪的事情,这使我思考。 即使目前尚不清楚得出哪些结论。 我最近提到,我们在GNU Tar中发现了一个错误,而这种情况是如何发生的就是这种情况。

对于备份文件服务器,我们使用Amanda和GNU Tar。 随着时间的流逝,我们偶尔会遇到一个非常罕见的问题,即当使用/var/mail目录备份文件系统时,tar会发疯,从而产生大量输出。 通常,此过程达到无穷远,必须杀死转储。 在其他情况下,它仍然以发出似乎完全压缩的TB数据结束。 当我再次遇到如此巨大的tar文件时,我对其进行了检查-发现它部分由零字节组成,而tar -t测试小组确实不喜欢该零字节,然后一切恢复正常。

(因此,我想知道空字节是否自然出现在邮箱中的人员中。事实证明, 在文本文件查找空字节不是那么简单,是的,它们在那里。)

我们最近将文件系统从/var/mail移到了Ubuntu 18.04下的新Linux文件服务器,因此切换到了比OmniOS计算机上更高且更标准的GNU Tar版本。 我们希望这可以解决我们的问题,但是同一事件几乎立即发生了。 这次,GNU Tar在Ubuntu机器上工作,我对所有可用的调试工具都很熟悉,因此我检查了正在运行的tar进程。 测试表明tar产生了一个无限的read()流,返回0字节:

 read(6, "", 512) = 0 read(6, "", 512) = 0 [...] read(6, "", 512) = 0 write(1, "\0\0\0\0\0"..., 10240) = 10240 read(6, "", 512) = 0 [...] 

lsof表示文件描述符6是其他人的邮箱。

我使用apt-get source tar下载了源代码,并开始在其中检查未检查文件完成情况的read()系统调用。 在检查了几级间接寻址之后,我发现一个显而易见的地方似乎省略了这种检查,即在sparse.cs文件的sparse_dump_region函数中。 然后我想起了什么。

几个月前, 我们在Alpine中遇到了NFS问题 。 处理此错误时,我跟踪了Alpine进程,并注意到它除其他外使用ftruncate()调整邮箱大小; 有时它会扩展它们,临时创建文件的稀疏部分,直到它填满为止,有时还压缩它。 这似乎与当前情况相吻合:连接了稀疏区域,使用ftruncate()减小文件大小会导致tar意外遇到文件完成的情况。

(这甚至可以解释为什么有时会还原tar;如果以后突然有新邮件到达邮箱,它将恢复到预期的大小,并且tar不再遇到意外的文件终止)。

我在GDB中混淆了我收到的Ubuntu调试符号和tar包源代码,并且能够重现该错误,尽管它与我的原始理论有所不同。 事实证明, sparse_dump_region不会重置文件的稀疏区域,但是会重置非稀疏区域(当然),并且如果您使用--sparse参数运行tar, --sparse用于所有文件(稀疏与否)。 因此,实际的错误是, 如果使用--sparse参数运行GNU Tar,并且在读取文件时将其压缩,则tar无法正确处理早于预期的文件结尾 。 如果文件再次增长,则tar将恢复。

(除非文件仅在末尾稀疏并且仅在此位置压缩。在这种情况下,一切都按顺序进行)。

我认为很多年前我可以在我们的OmniOS文件服务器上进行检查。 有多种方法可以跟踪程序和lsof类似物的系统调用,我可以找到并查看我的GNU Tar版本的源代码并使用OmniOS调试器运行它(尽管我们似乎未在其中安装GDB),依此类推。 但是我没有。 相反,我们耸了耸肩,继续前进。 我花了很多时间在Ubuntu下移动文件系统,这样我才能动动手指找出问题所在。

(这不仅与工具和环境有关;我们自动假定OmniOS拥有一些不受支持的旧版GNU Tar,对此进行调查是没有意义的,因为当然,该问题已在较新的版本中解决了)。

PS:也许,作为一种快速解决方案,我们只是禁止Amanda在备份时使用tar --sparse 。 邮箱不应该稀疏,并且如果发生这种情况, 我们仍然会压缩文件系统备份 ,以使所有这些零字节得到很好的压缩。

PPS:我没有尝试将错误报告给GNU Tar开发人员,因为我仅在星期五才发现它,而大学现在正在放寒假。 随意在我之前做。

Source: https://habr.com/ru/post/zh-CN434624/


All Articles