]> Cypherpunks repositories - gostls13.git/commitdiff
archive/tar: fallback to pre-Go1.8 behavior on certain GNU files
authorJoe Tsai <joetsai@digital-static.net>
Tue, 8 Aug 2017 00:58:43 +0000 (17:58 -0700)
committerJoe Tsai <thebrokentoaster@gmail.com>
Fri, 11 Aug 2017 03:11:49 +0000 (03:11 +0000)
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>
src/archive/tar/reader.go
src/archive/tar/reader_test.go
src/archive/tar/testdata/invalid-go17.tar [new file with mode: 0644]

index 9abe888218f8b24c7a18574b2e272414062ccccb..21c0330420a1ca8ce418089757665463bbf6a4de 100644 (file)
@@ -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
index 338686836b6337381bd02ecf0d0cc453594fbcc5..c4fda9d8cddcdc8ba97f88280fdea0b42bf403a6 100644 (file)
@@ -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 {
diff --git a/src/archive/tar/testdata/invalid-go17.tar b/src/archive/tar/testdata/invalid-go17.tar
new file mode 100644 (file)
index 0000000..58f2488
Binary files /dev/null and b/src/archive/tar/testdata/invalid-go17.tar differ