Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,25 @@ func TestExtractAllTarGz(t *testing.T) {
assertFileContent(t, filepath.Join(dir, "lib", "util.js"), "module.exports = {};")
}

func TestExtractAllSkipsGlobalPAXHeaders(t *testing.T) {
data := gzipTar(t, tarWithGlobalPAXHeaders(t, "repo-main/"))
r, err := OpenBytes("test.tar.gz", data)
if err != nil {
t.Fatal(err)
}
defer func() { _ = r.Close() }()
dir := t.TempDir()
if err := ExtractAll(r, dir); err != nil {
t.Fatal(err)
}
entries, err := os.ReadDir(dir)
if err != nil || len(entries) != 1 || entries[0].Name() != "repo-main" {
t.Fatalf("extracted root = %v, %v", entries, err)
}
assertFileContent(t, filepath.Join(dir, "repo-main", "README.md"), "body: README.md")
assertFileContent(t, filepath.Join(dir, "repo-main", "pax_global_header"), "body: pax_global_header")
}

func TestExtractAllCreatesTargetDir(t *testing.T) {
reader, err := OpenBytes("test.zip", createTestZip())
if err != nil {
Expand Down
19 changes: 12 additions & 7 deletions stream_tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@ func newTarStream(ctx context.Context, content io.Reader, compression string) (*
}

func (t *tarStream) Next() (*StreamEntry, error) {
h, err := t.reader.Next()
if err != nil {
return nil, err
for {
h, err := t.reader.Next()
if err != nil {
return nil, err
}
if h.Typeflag == tar.TypeXGlobalHeader {
continue
}
return &StreamEntry{
FileInfo: fileInfoFromTar(h), Linkname: h.Linkname,
IsHardlink: h.Typeflag == tar.TypeLink,
}, nil
}
return &StreamEntry{
FileInfo: fileInfoFromTar(h), Linkname: h.Linkname,
IsHardlink: h.Typeflag == tar.TypeLink,
}, nil
}

func (t *tarStream) Read(p []byte) (int, error) {
Expand Down
3 changes: 3 additions & 0 deletions tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ func openTarWithInitialEntryCount(raw []byte, compression string, initialEntryCo
if err != nil {
return nil, fmt.Errorf("reading tar: %w", err)
}
if header.Typeflag == tar.TypeXGlobalHeader {
continue
}
if err := checkArchiveEntryCount(initialEntryCount + len(files) + 1); err != nil {
return nil, err
}
Expand Down
122 changes: 122 additions & 0 deletions tar_pax_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package archives

import (
"archive/tar"
"bytes"
"io"
"reflect"
"testing"
)

func tarWithGlobalPAXHeaders(t *testing.T, prefix string) []byte {
t.Helper()
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
for _, name := range []string{"README.md", "pax_global_header", ""} {
if err := tw.WriteHeader(&tar.Header{
Name: "pax_global_header",
Typeflag: tar.TypeXGlobalHeader,
PAXRecords: map[string]string{"comment": "0123456789abcdef0123456789abcdef01234567"},
}); err != nil {
t.Fatal(err)
}
if name != "" {
writeTarFile(t, tw, prefix+name, "body: "+name, 0o644)
}
}
if err := tw.Close(); err != nil {
t.Fatal(err)
}
return buf.Bytes()
}

func TestTarSkipsGlobalPAXHeaders(t *testing.T) {
raw := tarWithGlobalPAXHeaders(t, "")
for _, name := range []string{"test.tar", "test.tar.gz", "test.tar.xz", "test.tar.zst", "test.gem", "test.conda"} {
t.Run(name, func(t *testing.T) {
data := streamTestArchive(t, name, raw)
r, err := OpenBytes(name, data)
if err != nil {
t.Fatal(err)
}
defer func() { _ = r.Close() }()
files, err := r.List()
if err != nil {
t.Fatal(err)
}
var paths []string
for _, file := range files {
paths = append(paths, file.Path)
body, err := r.Extract(file.Path)
if err != nil {
t.Fatal(err)
}
got, err := io.ReadAll(body)
_ = body.Close()
if err != nil || string(got) != "body: "+file.Path {
t.Fatalf("Extract(%q) = %q, %v", file.Path, got, err)
}
}
if !reflect.DeepEqual(paths, []string{"README.md", "pax_global_header"}) {
t.Fatalf("paths = %v", paths)
}
})
}
}

func TestStreamSkipsGlobalPAXHeaders(t *testing.T) {
raw := tarWithGlobalPAXHeaders(t, "")
for _, name := range []string{"test.tar", "test.tar.gz", "test.tar.xz", "test.tar.zst", "test.gem", "test.conda"} {
t.Run(name, func(t *testing.T) {
data := streamTestArchive(t, name, raw)
s, err := OpenStream(name, bytes.NewReader(data), StreamOptions{MaxEntries: 2})
if err != nil {
t.Fatal(err)
}
defer func() { _ = s.Close() }()
for _, path := range []string{"README.md", "pax_global_header"} {
entry, err := s.Next()
if err != nil {
t.Fatal(err)
}
if entry.Path != path {
t.Fatalf("path = %q, want %q", entry.Path, path)
}
body, err := io.ReadAll(s)
if err != nil || string(body) != "body: "+path {
t.Fatalf("Read(%q) = %q, %v", path, body, err)
}
}
if _, err := s.Next(); err != io.EOF {
t.Fatalf("end = %v, want EOF", err)
}
})
}
}

func TestBrowserRootWithGlobalPAXHeaders(t *testing.T) {
data := gzipTar(t, tarWithGlobalPAXHeaders(t, "repo-main/"))
r, err := OpenBytes("test.tar.gz", data)
if err != nil {
t.Fatal(err)
}
defer func() { _ = r.Close() }()
files, err := r.List()
if err != nil {
t.Fatal(err)
}
if root := CommonRoot(files); root != "repo-main/" {
t.Fatalf("CommonRoot = %q", root)
}
reopen := func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(data)), nil }
b, err := OpenBrowser("test.tar.gz", bytes.NewReader(data), reopen, BrowseOptions{DetectRoot: true})
if err != nil {
t.Fatal(err)
}
files, err = b.ListDir("")
if err != nil || len(files) != 2 || files[0].Path != "README.md" || files[1].Path != "pax_global_header" {
t.Fatalf("ListDir = %+v, %v", files, err)
}
assertBrowsedBody(t, b, "README.md", "body: README.md")
assertBrowsedBody(t, b, "pax_global_header", "body: pax_global_header")
}
Loading