forked from mirrors/tar-split
archive/tar: detect truncated files
Motivation: * Reader.skipUnread never reports io.ErrUnexpectedEOF. This is strange given that io.ErrUnexpectedEOF is given through Reader.Read if the user manually reads the file. * Reader.skipUnread fails to detect truncated files since io.Seeker is lazy about reporting errors. Thus, the behavior of Reader differs whether the input io.Reader also satisfies io.Seeker or not. To solve this, we seek to one before the end of the data section and always rely on at least one call to io.CopyN. If the tr.r satisfies io.Seeker, this is guarunteed to never read more than blockSize. Fixes #12557 Change-Id: I0ddddfc6bed0d74465cb7e7a02b26f1de7a7a279 Reviewed-on: https://go-review.googlesource.com/15175 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
cf83c95de8
commit
bffda594f7
2 changed files with 64 additions and 10 deletions
|
@ -12,8 +12,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -288,11 +288,11 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
|
||||||
// succeed, and seems harmless enough.
|
// succeed, and seems harmless enough.
|
||||||
ext.ModTime = hdr.ModTime
|
ext.ModTime = hdr.ModTime
|
||||||
// The spec asks that we namespace our pseudo files
|
// The spec asks that we namespace our pseudo files
|
||||||
// with the current pid.
|
// with the current pid. However, this results in differing outputs
|
||||||
pid := os.Getpid()
|
// for identical inputs. As such, the constant 0 is now used instead.
|
||||||
|
// golang.org/issue/12358
|
||||||
dir, file := path.Split(hdr.Name)
|
dir, file := path.Split(hdr.Name)
|
||||||
fullName := path.Join(dir,
|
fullName := path.Join(dir, "PaxHeaders.0", file)
|
||||||
fmt.Sprintf("PaxHeaders.%d", pid), file)
|
|
||||||
|
|
||||||
ascii := toASCII(fullName)
|
ascii := toASCII(fullName)
|
||||||
if len(ascii) > 100 {
|
if len(ascii) > 100 {
|
||||||
|
@ -302,8 +302,15 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
|
||||||
// Construct the body
|
// Construct the body
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
for k, v := range paxHeaders {
|
// Keys are sorted before writing to body to allow deterministic output.
|
||||||
fmt.Fprint(&buf, paxHeader(k+"="+v))
|
var keys []string
|
||||||
|
for k := range paxHeaders {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
fmt.Fprint(&buf, paxHeader(k+"="+paxHeaders[k]))
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.Size = int64(len(buf.Bytes()))
|
ext.Size = int64(len(buf.Bytes()))
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"testing/iotest"
|
"testing/iotest"
|
||||||
|
@ -291,7 +292,7 @@ func TestPax(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Simple test to make sure PAX extensions are in effect
|
// Simple test to make sure PAX extensions are in effect
|
||||||
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
|
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
|
||||||
t.Fatal("Expected at least one PAX header to be written.")
|
t.Fatal("Expected at least one PAX header to be written.")
|
||||||
}
|
}
|
||||||
// Test that we can get a long name back out of the archive.
|
// Test that we can get a long name back out of the archive.
|
||||||
|
@ -330,7 +331,7 @@ func TestPaxSymlink(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Simple test to make sure PAX extensions are in effect
|
// Simple test to make sure PAX extensions are in effect
|
||||||
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
|
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
|
||||||
t.Fatal("Expected at least one PAX header to be written.")
|
t.Fatal("Expected at least one PAX header to be written.")
|
||||||
}
|
}
|
||||||
// Test that we can get a long name back out of the archive.
|
// Test that we can get a long name back out of the archive.
|
||||||
|
@ -380,7 +381,7 @@ func TestPaxNonAscii(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Simple test to make sure PAX extensions are in effect
|
// Simple test to make sure PAX extensions are in effect
|
||||||
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
|
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
|
||||||
t.Fatal("Expected at least one PAX header to be written.")
|
t.Fatal("Expected at least one PAX header to be written.")
|
||||||
}
|
}
|
||||||
// Test that we can get a long name back out of the archive.
|
// Test that we can get a long name back out of the archive.
|
||||||
|
@ -439,6 +440,52 @@ func TestPaxXattrs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPaxHeadersSorted(t *testing.T) {
|
||||||
|
fileinfo, err := os.Stat("testdata/small.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
hdr, err := FileInfoHeader(fileinfo, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("os.Stat: %v", err)
|
||||||
|
}
|
||||||
|
contents := strings.Repeat(" ", int(hdr.Size))
|
||||||
|
|
||||||
|
hdr.Xattrs = map[string]string{
|
||||||
|
"foo": "foo",
|
||||||
|
"bar": "bar",
|
||||||
|
"baz": "baz",
|
||||||
|
"qux": "qux",
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
writer := NewWriter(&buf)
|
||||||
|
if err := writer.WriteHeader(hdr); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err = writer.Write([]byte(contents)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Simple test to make sure PAX extensions are in effect
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
|
||||||
|
t.Fatal("Expected at least one PAX header to be written.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// xattr bar should always appear before others
|
||||||
|
indices := []int{
|
||||||
|
bytes.Index(buf.Bytes(), []byte("bar=bar")),
|
||||||
|
bytes.Index(buf.Bytes(), []byte("baz=baz")),
|
||||||
|
bytes.Index(buf.Bytes(), []byte("foo=foo")),
|
||||||
|
bytes.Index(buf.Bytes(), []byte("qux=qux")),
|
||||||
|
}
|
||||||
|
if !sort.IntsAreSorted(indices) {
|
||||||
|
t.Fatal("PAX headers are not sorted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPAXHeader(t *testing.T) {
|
func TestPAXHeader(t *testing.T) {
|
||||||
medName := strings.Repeat("CD", 50)
|
medName := strings.Repeat("CD", 50)
|
||||||
longName := strings.Repeat("AB", 100)
|
longName := strings.Repeat("AB", 100)
|
||||||
|
|
Loading…
Reference in a new issue