由多伦多大学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开发人员,因为我仅在星期五才发现它,而大学现在正在放寒假。 随意在我之前做。