mirror of
https://github.com/golang/go
synced 2024-11-23 15:30:05 -07:00
archive/tar: fallback to pre-Go1.8 behavior on certain GNU files
Prior to Go1.8, the Writer had a bug where it would output an invalid tar file in certain rare situations because the logic incorrectly believed that the old GNU format had a prefix field. This is wrong and leads to an output file that mangles the atime and ctime fields, which are often left unused. In order to continue reading tar files created by former, buggy versions of Go, we skeptically parse the atime and ctime fields. If we are unable to parse them and the prefix field looks like an ASCII string, then we fallback on the pre-Go1.8 behavior of treating these fields as the USTAR prefix field. Note that this will not use the fallback logic for all possible files generated by a pre-Go1.8 toolchain. If the generated file happened to have a prefix field that parses as valid atime and ctime fields (e.g., when they are valid octal strings), then it is impossible to distinguish between an valid GNU file and an invalid pre-Go1.8 file. Fixes #21005 Change-Id: Iebf5c67c08e0e46da6ee41a2e8b339f84030dd90 Reviewed-on: https://go-review.googlesource.com/53635 Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
b8519cd739
commit
01e45c7368
@ -463,26 +463,6 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
|
||||
hdr.Typeflag = v7.TypeFlag()[0]
|
||||
hdr.Linkname = p.parseString(v7.LinkName())
|
||||
|
||||
// The atime and ctime fields are often left unused. Some versions of Go
|
||||
// had a bug in the tar.Writer where it would output an invalid tar file
|
||||
// in certain rare situations because the logic incorrectly believed that
|
||||
// the old GNU format had a prefix field. This is wrong and leads to
|
||||
// an outputted file that actually mangles the atime and ctime fields.
|
||||
//
|
||||
// In order to continue reading tar files created by a buggy writer, we
|
||||
// try to parse the atime and ctime fields, but just return the zero value
|
||||
// of time.Time when we cannot parse them.
|
||||
//
|
||||
// See https://golang.org/issues/12594
|
||||
tryParseTime := func(b []byte) time.Time {
|
||||
var p parser
|
||||
n := p.parseNumeric(b)
|
||||
if b[0] != 0x00 && p.err == nil {
|
||||
return time.Unix(n, 0)
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Unpack format specific fields.
|
||||
if format > formatV7 {
|
||||
ustar := tr.blk.USTAR()
|
||||
@ -504,9 +484,43 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
|
||||
hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0)
|
||||
hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0)
|
||||
case formatGNU:
|
||||
var p2 parser
|
||||
gnu := tr.blk.GNU()
|
||||
hdr.AccessTime = tryParseTime(gnu.AccessTime())
|
||||
hdr.ChangeTime = tryParseTime(gnu.ChangeTime())
|
||||
if b := gnu.AccessTime(); b[0] != 0 {
|
||||
hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0)
|
||||
}
|
||||
if b := gnu.ChangeTime(); b[0] != 0 {
|
||||
hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0)
|
||||
}
|
||||
|
||||
// Prior to Go1.8, the Writer had a bug where it would output
|
||||
// an invalid tar file in certain rare situations because the logic
|
||||
// incorrectly believed that the old GNU format had a prefix field.
|
||||
// This is wrong and leads to an output file that mangles the
|
||||
// atime and ctime fields, which are often left unused.
|
||||
//
|
||||
// In order to continue reading tar files created by former, buggy
|
||||
// versions of Go, we skeptically parse the atime and ctime fields.
|
||||
// If we are unable to parse them and the prefix field looks like
|
||||
// an ASCII string, then we fallback on the pre-Go1.8 behavior
|
||||
// of treating these fields as the USTAR prefix field.
|
||||
//
|
||||
// Note that this will not use the fallback logic for all possible
|
||||
// files generated by a pre-Go1.8 toolchain. If the generated file
|
||||
// happened to have a prefix field that parses as valid
|
||||
// atime and ctime fields (e.g., when they are valid octal strings),
|
||||
// then it is impossible to distinguish between an valid GNU file
|
||||
// and an invalid pre-Go1.8 file.
|
||||
//
|
||||
// See https://golang.org/issues/12594
|
||||
// See https://golang.org/issues/21005
|
||||
if p2.err != nil {
|
||||
hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{}
|
||||
ustar := tr.blk.USTAR()
|
||||
if s := p.parseString(ustar.Prefix()); isASCII(s) {
|
||||
prefix = s
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(prefix) > 0 {
|
||||
hdr.Name = prefix + "/" + hdr.Name
|
||||
|
@ -346,6 +346,15 @@ func TestReader(t *testing.T) {
|
||||
}, {
|
||||
file: "testdata/issue12435.tar",
|
||||
err: ErrHeader,
|
||||
}, {
|
||||
// Ensure that we can read back the original Header as written with
|
||||
// a buggy pre-Go1.8 tar.Writer.
|
||||
file: "testdata/invalid-go17.tar",
|
||||
headers: []*Header{{
|
||||
Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
|
||||
Uid: 010000000,
|
||||
ModTime: time.Unix(0, 0),
|
||||
}},
|
||||
}}
|
||||
|
||||
for i, v := range vectors {
|
||||
|
BIN
src/archive/tar/testdata/invalid-go17.tar
vendored
Normal file
BIN
src/archive/tar/testdata/invalid-go17.tar
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user