你正在使用的Go版本是(go version
)?
go version go1.12.5 darwin/amd64
这个问题在最新版本的发布中是否重现?
是的
你做了什么?
读取由git archive
生成的tar文件:
$ git clone https://github.com/SSW-SCIENTIFIC/NNDD.git
$ cd NNDD
$ git archive --format tar c21b98da2ca7f007230e696b2eda5da6589fe137 > nndd.tar
或者,你可以直接从github获取这个归档文件: https://github.com/SSW-SCIENTIFIC/NNDD/tarball/c21b98da2ca7f007230e696b2eda5da6589fe137
如果你尝试用golang的archive/tar
读取这个文件,你会在一个条目上得到一个tar.ErrHeader
。这里有一个重现程序:
package main
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"log"
"net/http"
"os"
)
func main() {
// Read tar from file if specified, otherwise fetch directly from github
var r io.Reader
if len(os.Args) > 1 {
f, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
defer f.Close()
r = f
} else {
resp, err := http.Get("https://github.com/SSW-SCIENTIFIC/NNDD/tarball/c21b98da2ca7f007230e696b2eda5da6589fe137")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
gr, err := gzip.NewReader(resp.Body)
if err != nil {
log.Fatal(err)
}
defer gr.Close()
r = gr
}
tr := tar.NewReader(r)
for {
_, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
fmt.Println("Successfully read tar")
}
如果你尝试用bsdtar或gnutar读取相同的文件,它们都能读取整个tarball。bsdtar(取决于版本)会在stderr上记录一个警告,但仍然提取tarball的其余部分。
归档文件包含一个符号链接,该链接的值不是路径,而是一个包含空字符的大值。validatePAXRecord
明确禁止在PAX值的链接中出现空字符。这导致tar.Reader.Next()
返回tar.ErrHeader
。
https://github.com/golang/go/blob/go1.12.5/src/archive/tar/strconv.go#L321-L322
我向git邮件列表报告了这个问题: https://public-inbox.org/git/CAMVcy0Q0TL6uEGR2NeudJrOiXdQ87XcducL0EwMidWucjk5XYw@mail.gmail.com/T/#u 阅读该问题以获取有关归档文件中“错误”条目的更多详细信息。
那里的观点似乎是,为链接生成非C字符串值是有效的。并非所有的tarball都注定用于文件系统。而且最新的bsdtar和gnutar都没有对值提出抱怨。gnutar将写入符号链接值直到第一个空字符。
你期望看到什么?
我认为以下任何一种行为都会更好:
- 在遇到可恢复错误时,能够读取tar的其余部分。
- 在所有PAX值中允许空字符。
- 将PAX值截断到第一个空字符(与gnutar保持一致)。
你看到了什么?
在bsdtar和gnutar都可以读取的tarball上出现了tar.ErrHeader
。
5条答案
按热度按时间nue99wik1#
@dsnet
wj8zmpe12#
FYI,我很高兴为这个问题提供一个修复方案,只是需要指导一下适当的修复方法。
lokaqttq3#
关于
linkpath
的PAX规范说明:正在创建到已归档的任何类型文件的链接的路径名。此记录应覆盖以下ustar头块中的链接名字段。以下ustar头块应确定创建的链接类型。如果以下头块的typeflag为1,则为硬链接。如果typeflag为2,则为符号链接,且linkpath值应为符号链接的内容。pax实用程序应将链接(符号链接的内容)的名称从头中的编码转换为适用于本地文件系统的字符集。当在写入或复制模式下使用时,pax应包括一个无法完全用便携式字符集除NUL外的其他成员表示的每个链接的linkpath扩展记录。
这表明NUL不是
linkpath
扩展记录的有效字符。monwx1rj4#
如果遇到可恢复的错误,能够读取tar文件的其余部分。
这似乎合理,但需要对整个实现进行扫描以识别致命错误和非致命错误。它还需要一个API设计来显示非致命错误。
一个问题是,实现始终为所有类型的解析错误(无论是致命还是非致命错误)返回
tar.ErrHeader
。技术上讲,兼容性协议要求我们在相同情况下继续返回相同的哨兵错误。我不知道谁使用这个晦涩的功能。9gm1akwq5#
当在写入或复制模式下使用时,pax应为每个路径名不能完全用便携式字符集除NUL外的成员表示的链接包含一个链接路径扩展头记录。这意味着NUL不是出现在
linkpath
扩展记录中的有效字符。我读了几次规范。我认为它的意思是在set(linkpath) - (set(portable_characters) - set(\0)) != set()
的情况下使用扩展头记录。使用集合符号来澄清歧义。按照这种解释,可以包含NUL。然而,规范还提到了UTF-8。但是
archive/tar
实现由于许多存档没有遵循规范而无法验证这一点。我认为这里也可以做出同样的论断。特别是考虑到gnutar和bsdtar对此没有问题。例如,引入validatePAXRecord
的CR引用了gnutar和bsdtar在实践中的实现方式。能够读取遇到可恢复错误的其余tar文件。这似乎合理,但需要对整个实现进行彻底检查以确定致命错误和非致命错误。还需要设计API以显示非致命错误。
一个问题是,实现始终为所有类型的解析错误(无论是致命还是非致命错误)返回
tar.ErrHeader
。技术上,兼容性协议要求我们在相同情况下继续返回相同的哨兵错误。我不知道谁会使用这个晦涩的功能。我同意,我认为在没有进一步调查的情况下排除了这种可能性。鉴于gnutar和bsdtar的做法,我会倾向于只是删除包含NUL的验证。如果这样做了,对现有用例的影响:
tar.ErrHeader
。