go 归档/tar:不处理带有空值的扩展pax值

gwbalxhn  于 5个月前  发布在  Go
关注(0)|答案(5)|浏览(50)

你正在使用的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

wj8zmpe1

wj8zmpe12#

FYI,我很高兴为这个问题提供一个修复方案,只是需要指导一下适当的修复方法。

lokaqttq

lokaqttq3#

关于linkpath的PAX规范说明:
正在创建到已归档的任何类型文件的链接的路径名。此记录应覆盖以下ustar头块中的链接名字段。以下ustar头块应确定创建的链接类型。如果以下头块的typeflag为1,则为硬链接。如果typeflag为2,则为符号链接,且linkpath值应为符号链接的内容。pax实用程序应将链接(符号链接的内容)的名称从头中的编码转换为适用于本地文件系统的字符集。当在写入或复制模式下使用时,pax应包括一个无法完全用便携式字符集除NUL外的其他成员表示的每个链接的linkpath扩展记录。
这表明NUL不是linkpath扩展记录的有效字符。

monwx1rj

monwx1rj4#

如果遇到可恢复的错误,能够读取tar文件的其余部分。
这似乎合理,但需要对整个实现进行扫描以识别致命错误和非致命错误。它还需要一个API设计来显示非致命错误。
一个问题是,实现始终为所有类型的解析错误(无论是致命还是非致命错误)返回tar.ErrHeader。技术上讲,兼容性协议要求我们在相同情况下继续返回相同的哨兵错误。我不知道谁使用这个晦涩的功能。

9gm1akwq

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文件。当读取这些tar文件时,如何处理取决于实现。我认为这是可以接受的,因为这与不写入有效的utf-8相同。此外,我怀疑有人依赖于带有NUL的linkpath的行为。
  • 阅读者:我们不会失败,而是将带有NUL的linkpath传递下去。这可能导致程序后续部分的失败。我认为这是可以接受的,因为它本来就会失败。此外,错误更有可能是描述性的,而不是错误值tar.ErrHeader

相关问题