]> Cypherpunks repositories - gostls13.git/commitdiff
archive/zip: permit zip files to have prefixes
authorIan Lance Taylor <iant@golang.org>
Fri, 25 Feb 2022 03:23:17 +0000 (19:23 -0800)
committerGopher Robot <gobot@golang.org>
Sun, 8 May 2022 17:26:10 +0000 (17:26 +0000)
A Java jar file is a zip file, but it can have a prefix that is a bash
script that unpacks the zip file. Most zip programs ignore such prefixes.
This CL changes the archive/zip package to do the same.

Fixes #10464
Fixes #51337

Change-Id: I976e9c64684644317bd21077bc5b4a2baf626ee6
Reviewed-on: https://go-review.googlesource.com/c/go/+/387976
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Joseph Tsai <joetsai@digital-static.net>
Auto-Submit: Ian Lance Taylor <iant@google.com>

src/archive/zip/reader.go
src/archive/zip/reader_test.go
src/archive/zip/testdata/readme.notzip
src/archive/zip/testdata/test-prefix.zip [new file with mode: 0644]

index 19a9c3b2dba691f5cfa94ba829bd43c136d82cd0..d875c7be252c7b4c30dd3af3eb67dddcafdc6deb 100644 (file)
@@ -33,6 +33,10 @@ type Reader struct {
        Comment       string
        decompressors map[uint16]Decompressor
 
+       // Some JAR files are zip files with a prefix that is a bash script.
+       // The baseOffset field is the start of the zip file proper.
+       baseOffset int64
+
        // fileList is a list of files sorted by ename,
        // for use by the Open method.
        fileListOnce sync.Once
@@ -52,7 +56,7 @@ type File struct {
        FileHeader
        zip          *Reader
        zipr         io.ReaderAt
-       headerOffset int64
+       headerOffset int64 // includes overall ZIP archive baseOffset
        zip64        bool // zip64 extended information extra field presence
 }
 
@@ -90,11 +94,12 @@ func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
 }
 
 func (z *Reader) init(r io.ReaderAt, size int64) error {
-       end, err := readDirectoryEnd(r, size)
+       end, baseOffset, err := readDirectoryEnd(r, size)
        if err != nil {
                return err
        }
        z.r = r
+       z.baseOffset = baseOffset
        // Since the number of directory records is not validated, it is not
        // safe to preallocate z.File without first checking that the specified
        // number of files is reasonable, since a malformed archive may
@@ -106,7 +111,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
        }
        z.Comment = end.comment
        rs := io.NewSectionReader(r, 0, size)
-       if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil {
+       if _, err = rs.Seek(z.baseOffset+int64(end.directoryOffset), io.SeekStart); err != nil {
                return err
        }
        buf := bufio.NewReader(rs)
@@ -124,6 +129,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
                if err != nil {
                        return err
                }
+               f.headerOffset += z.baseOffset
                z.File = append(z.File, f)
        }
        if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here
@@ -494,7 +500,7 @@ func readDataDescriptor(r io.Reader, f *File) error {
        return nil
 }
 
-func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
+func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, baseOffset int64, err error) {
        // look for directoryEndSignature in the last 1k, then in the last 65k
        var buf []byte
        var directoryEndOffset int64
@@ -504,7 +510,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
                }
                buf = make([]byte, int(bLen))
                if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF {
-                       return nil, err
+                       return nil, 0, err
                }
                if p := findSignatureInBlock(buf); p >= 0 {
                        buf = buf[p:]
@@ -512,7 +518,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
                        break
                }
                if i == 1 || bLen == size {
-                       return nil, ErrFormat
+                       return nil, 0, ErrFormat
                }
        }
 
@@ -529,7 +535,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
        }
        l := int(d.commentLen)
        if l > len(b) {
-               return nil, errors.New("zip: invalid comment length")
+               return nil, 0, errors.New("zip: invalid comment length")
        }
        d.comment = string(b[:l])
 
@@ -537,17 +543,21 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
        if d.directoryRecords == 0xffff || d.directorySize == 0xffff || d.directoryOffset == 0xffffffff {
                p, err := findDirectory64End(r, directoryEndOffset)
                if err == nil && p >= 0 {
+                       directoryEndOffset = p
                        err = readDirectory64End(r, p, d)
                }
                if err != nil {
-                       return nil, err
+                       return nil, 0, err
                }
        }
+
+       baseOffset = directoryEndOffset - int64(d.directorySize) - int64(d.directoryOffset)
+
        // Make sure directoryOffset points to somewhere in our file.
-       if o := int64(d.directoryOffset); o < 0 || o >= size {
-               return nil, ErrFormat
+       if o := baseOffset + int64(d.directoryOffset); o < 0 || o >= size {
+               return nil, 0, ErrFormat
        }
-       return d, nil
+       return d, baseOffset, nil
 }
 
 // findDirectory64End tries to read the zip64 locator just before the
index fd0a171304ce14a47e2528d9d89edd9432b8a61e..4c1e82b9d4c273b2533e898eeb7da8187dd5366c 100644 (file)
@@ -90,6 +90,24 @@ var tests = []ZipTest{
                        },
                },
        },
+       {
+               Name:    "test-prefix.zip",
+               Comment: "This is a zipfile comment.",
+               File: []ZipTestFile{
+                       {
+                               Name:     "test.txt",
+                               Content:  []byte("This is a test text file.\n"),
+                               Modified: time.Date(2010, 9, 5, 12, 12, 1, 0, timeZone(+10*time.Hour)),
+                               Mode:     0644,
+                       },
+                       {
+                               Name:     "gophercolor16x16.png",
+                               File:     "gophercolor16x16.png",
+                               Modified: time.Date(2010, 9, 5, 15, 52, 58, 0, timeZone(+10*time.Hour)),
+                               Mode:     0644,
+                       },
+               },
+       },
        {
                Name:   "r.zip",
                Source: returnRecursiveZip,
@@ -1011,7 +1029,7 @@ func TestIssue10957(t *testing.T) {
                "\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
                "00000000PK\x01\x0200000000" +
                "0000000000000000\v\x00\x00\x00" +
-               "\x00\x0000PK\x05\x06000000\x05\x000000" +
+               "\x00\x0000PK\x05\x06000000\x05\x00\xfd\x00\x00\x00" +
                "\v\x00\x00\x00\x00\x00")
        z, err := NewReader(bytes.NewReader(data), int64(len(data)))
        if err != nil {
@@ -1056,7 +1074,7 @@ func TestIssue11146(t *testing.T) {
                "0000000000000000PK\x01\x02" +
                "0000\b0\b\x00000000000000" +
                "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000PK\x05\x06\x00\x00" +
-               "\x00\x0000\x01\x0000008\x00\x00\x00\x00\x00")
+               "\x00\x0000\x01\x00\x26\x00\x00\x008\x00\x00\x00\x00\x00")
        z, err := NewReader(bytes.NewReader(data), int64(len(data)))
        if err != nil {
                t.Fatal(err)
index 81737275c6ebf5ea69b992753ab4050f031f31b8..79b1cb6de33c6ae86451acedbd50df4207a5710e 100644 (file)
Binary files a/src/archive/zip/testdata/readme.notzip and b/src/archive/zip/testdata/readme.notzip differ
diff --git a/src/archive/zip/testdata/test-prefix.zip b/src/archive/zip/testdata/test-prefix.zip
new file mode 100644 (file)
index 0000000..1eabb48
Binary files /dev/null and b/src/archive/zip/testdata/test-prefix.zip differ