diff options
Diffstat (limited to 'libgo/go/archive')
-rw-r--r-- | libgo/go/archive/tar/common.go | 40 | ||||
-rw-r--r-- | libgo/go/archive/tar/format.go | 138 | ||||
-rw-r--r-- | libgo/go/archive/tar/fuzz_test.go | 80 | ||||
-rw-r--r-- | libgo/go/archive/tar/reader.go | 102 | ||||
-rw-r--r-- | libgo/go/archive/tar/reader_test.go | 30 | ||||
-rw-r--r-- | libgo/go/archive/tar/stat_actime1.go | 1 | ||||
-rw-r--r-- | libgo/go/archive/tar/stat_actime2.go | 1 | ||||
-rw-r--r-- | libgo/go/archive/tar/stat_unix.go | 1 | ||||
-rw-r--r-- | libgo/go/archive/tar/strconv.go | 43 | ||||
-rw-r--r-- | libgo/go/archive/tar/tar_test.go | 2 | ||||
-rw-r--r-- | libgo/go/archive/tar/writer.go | 89 | ||||
-rw-r--r-- | libgo/go/archive/tar/writer_test.go | 24 | ||||
-rw-r--r-- | libgo/go/archive/zip/fuzz_test.go | 81 | ||||
-rw-r--r-- | libgo/go/archive/zip/reader.go | 106 | ||||
-rw-r--r-- | libgo/go/archive/zip/reader_test.go | 248 | ||||
-rw-r--r-- | libgo/go/archive/zip/struct.go | 10 | ||||
-rw-r--r-- | libgo/go/archive/zip/writer_test.go | 2 |
17 files changed, 552 insertions, 446 deletions
diff --git a/libgo/go/archive/tar/common.go b/libgo/go/archive/tar/common.go index c667cfc..c99b5c1 100644 --- a/libgo/go/archive/tar/common.go +++ b/libgo/go/archive/tar/common.go @@ -316,10 +316,10 @@ func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { // fileState tracks the number of logical (includes sparse holes) and physical // (actual in tar archive) bytes remaining for the current file. // -// Invariant: LogicalRemaining >= PhysicalRemaining +// Invariant: logicalRemaining >= physicalRemaining type fileState interface { - LogicalRemaining() int64 - PhysicalRemaining() int64 + logicalRemaining() int64 + physicalRemaining() int64 } // allowedFormats determines which formats can be used. @@ -413,22 +413,22 @@ func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err // Check basic fields. var blk block - v7 := blk.V7() - ustar := blk.USTAR() - gnu := blk.GNU() - verifyString(h.Name, len(v7.Name()), "Name", paxPath) - verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) - verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) - verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) - verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) - verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) - verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) - verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) - verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) - verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) - verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) - verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) - verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) + v7 := blk.toV7() + ustar := blk.toUSTAR() + gnu := blk.toGNU() + verifyString(h.Name, len(v7.name()), "Name", paxPath) + verifyString(h.Linkname, len(v7.linkName()), "Linkname", paxLinkpath) + verifyString(h.Uname, len(ustar.userName()), "Uname", paxUname) + verifyString(h.Gname, len(ustar.groupName()), "Gname", paxGname) + verifyNumeric(h.Mode, len(v7.mode()), "Mode", paxNone) + verifyNumeric(int64(h.Uid), len(v7.uid()), "Uid", paxUid) + verifyNumeric(int64(h.Gid), len(v7.gid()), "Gid", paxGid) + verifyNumeric(h.Size, len(v7.size()), "Size", paxSize) + verifyNumeric(h.Devmajor, len(ustar.devMajor()), "Devmajor", paxNone) + verifyNumeric(h.Devminor, len(ustar.devMinor()), "Devminor", paxNone) + verifyTime(h.ModTime, len(v7.modTime()), "ModTime", paxMtime) + verifyTime(h.AccessTime, len(gnu.accessTime()), "AccessTime", paxAtime) + verifyTime(h.ChangeTime, len(gnu.changeTime()), "ChangeTime", paxCtime) // Check for header-only types. var whyOnlyPAX, whyOnlyGNU string @@ -538,7 +538,7 @@ type headerFileInfo struct { func (fi headerFileInfo) Size() int64 { return fi.h.Size } func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } -func (fi headerFileInfo) Sys() interface{} { return fi.h } +func (fi headerFileInfo) Sys() any { return fi.h } // Name returns the base name of the file. func (fi headerFileInfo) Name() string { diff --git a/libgo/go/archive/tar/format.go b/libgo/go/archive/tar/format.go index cfe24a5..21b9d9d 100644 --- a/libgo/go/archive/tar/format.go +++ b/libgo/go/archive/tar/format.go @@ -156,28 +156,28 @@ var zeroBlock block type block [blockSize]byte // Convert block to any number of formats. -func (b *block) V7() *headerV7 { return (*headerV7)(b) } -func (b *block) GNU() *headerGNU { return (*headerGNU)(b) } -func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) } -func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) } -func (b *block) Sparse() sparseArray { return sparseArray(b[:]) } +func (b *block) toV7() *headerV7 { return (*headerV7)(b) } +func (b *block) toGNU() *headerGNU { return (*headerGNU)(b) } +func (b *block) toSTAR() *headerSTAR { return (*headerSTAR)(b) } +func (b *block) toUSTAR() *headerUSTAR { return (*headerUSTAR)(b) } +func (b *block) toSparse() sparseArray { return sparseArray(b[:]) } // GetFormat checks that the block is a valid tar header based on the checksum. // It then attempts to guess the specific format based on magic values. // If the checksum fails, then FormatUnknown is returned. -func (b *block) GetFormat() Format { +func (b *block) getFormat() Format { // Verify checksum. var p parser - value := p.parseOctal(b.V7().Chksum()) - chksum1, chksum2 := b.ComputeChecksum() + value := p.parseOctal(b.toV7().chksum()) + chksum1, chksum2 := b.computeChecksum() if p.err != nil || (value != chksum1 && value != chksum2) { return FormatUnknown } // Guess the magic values. - magic := string(b.USTAR().Magic()) - version := string(b.USTAR().Version()) - trailer := string(b.STAR().Trailer()) + magic := string(b.toUSTAR().magic()) + version := string(b.toUSTAR().version()) + trailer := string(b.toSTAR().trailer()) switch { case magic == magicUSTAR && trailer == trailerSTAR: return formatSTAR @@ -190,23 +190,23 @@ func (b *block) GetFormat() Format { } } -// SetFormat writes the magic values necessary for specified format +// setFormat writes the magic values necessary for specified format // and then updates the checksum accordingly. -func (b *block) SetFormat(format Format) { +func (b *block) setFormat(format Format) { // Set the magic values. switch { case format.has(formatV7): // Do nothing. case format.has(FormatGNU): - copy(b.GNU().Magic(), magicGNU) - copy(b.GNU().Version(), versionGNU) + copy(b.toGNU().magic(), magicGNU) + copy(b.toGNU().version(), versionGNU) case format.has(formatSTAR): - copy(b.STAR().Magic(), magicUSTAR) - copy(b.STAR().Version(), versionUSTAR) - copy(b.STAR().Trailer(), trailerSTAR) + copy(b.toSTAR().magic(), magicUSTAR) + copy(b.toSTAR().version(), versionUSTAR) + copy(b.toSTAR().trailer(), trailerSTAR) case format.has(FormatUSTAR | FormatPAX): - copy(b.USTAR().Magic(), magicUSTAR) - copy(b.USTAR().Version(), versionUSTAR) + copy(b.toUSTAR().magic(), magicUSTAR) + copy(b.toUSTAR().version(), versionUSTAR) default: panic("invalid format") } @@ -214,17 +214,17 @@ func (b *block) SetFormat(format Format) { // Update checksum. // This field is special in that it is terminated by a NULL then space. var f formatter - field := b.V7().Chksum() - chksum, _ := b.ComputeChecksum() // Possible values are 256..128776 + field := b.toV7().chksum() + chksum, _ := b.computeChecksum() // Possible values are 256..128776 f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143 field[7] = ' ' } -// ComputeChecksum computes the checksum for the header block. +// computeChecksum computes the checksum for the header block. // POSIX specifies a sum of the unsigned byte values, but the Sun tar used // signed byte values. // We compute and return both. -func (b *block) ComputeChecksum() (unsigned, signed int64) { +func (b *block) computeChecksum() (unsigned, signed int64) { for i, c := range b { if 148 <= i && i < 156 { c = ' ' // Treat the checksum field itself as all spaces. @@ -236,68 +236,68 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) { } // Reset clears the block with all zeros. -func (b *block) Reset() { +func (b *block) reset() { *b = block{} } type headerV7 [blockSize]byte -func (h *headerV7) Name() []byte { return h[000:][:100] } -func (h *headerV7) Mode() []byte { return h[100:][:8] } -func (h *headerV7) UID() []byte { return h[108:][:8] } -func (h *headerV7) GID() []byte { return h[116:][:8] } -func (h *headerV7) Size() []byte { return h[124:][:12] } -func (h *headerV7) ModTime() []byte { return h[136:][:12] } -func (h *headerV7) Chksum() []byte { return h[148:][:8] } -func (h *headerV7) TypeFlag() []byte { return h[156:][:1] } -func (h *headerV7) LinkName() []byte { return h[157:][:100] } +func (h *headerV7) name() []byte { return h[000:][:100] } +func (h *headerV7) mode() []byte { return h[100:][:8] } +func (h *headerV7) uid() []byte { return h[108:][:8] } +func (h *headerV7) gid() []byte { return h[116:][:8] } +func (h *headerV7) size() []byte { return h[124:][:12] } +func (h *headerV7) modTime() []byte { return h[136:][:12] } +func (h *headerV7) chksum() []byte { return h[148:][:8] } +func (h *headerV7) typeFlag() []byte { return h[156:][:1] } +func (h *headerV7) linkName() []byte { return h[157:][:100] } type headerGNU [blockSize]byte -func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) } -func (h *headerGNU) Magic() []byte { return h[257:][:6] } -func (h *headerGNU) Version() []byte { return h[263:][:2] } -func (h *headerGNU) UserName() []byte { return h[265:][:32] } -func (h *headerGNU) GroupName() []byte { return h[297:][:32] } -func (h *headerGNU) DevMajor() []byte { return h[329:][:8] } -func (h *headerGNU) DevMinor() []byte { return h[337:][:8] } -func (h *headerGNU) AccessTime() []byte { return h[345:][:12] } -func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] } -func (h *headerGNU) Sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) } -func (h *headerGNU) RealSize() []byte { return h[483:][:12] } +func (h *headerGNU) v7() *headerV7 { return (*headerV7)(h) } +func (h *headerGNU) magic() []byte { return h[257:][:6] } +func (h *headerGNU) version() []byte { return h[263:][:2] } +func (h *headerGNU) userName() []byte { return h[265:][:32] } +func (h *headerGNU) groupName() []byte { return h[297:][:32] } +func (h *headerGNU) devMajor() []byte { return h[329:][:8] } +func (h *headerGNU) devMinor() []byte { return h[337:][:8] } +func (h *headerGNU) accessTime() []byte { return h[345:][:12] } +func (h *headerGNU) changeTime() []byte { return h[357:][:12] } +func (h *headerGNU) sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) } +func (h *headerGNU) realSize() []byte { return h[483:][:12] } type headerSTAR [blockSize]byte -func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) } -func (h *headerSTAR) Magic() []byte { return h[257:][:6] } -func (h *headerSTAR) Version() []byte { return h[263:][:2] } -func (h *headerSTAR) UserName() []byte { return h[265:][:32] } -func (h *headerSTAR) GroupName() []byte { return h[297:][:32] } -func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] } -func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] } -func (h *headerSTAR) Prefix() []byte { return h[345:][:131] } -func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] } -func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] } -func (h *headerSTAR) Trailer() []byte { return h[508:][:4] } +func (h *headerSTAR) v7() *headerV7 { return (*headerV7)(h) } +func (h *headerSTAR) magic() []byte { return h[257:][:6] } +func (h *headerSTAR) version() []byte { return h[263:][:2] } +func (h *headerSTAR) userName() []byte { return h[265:][:32] } +func (h *headerSTAR) groupName() []byte { return h[297:][:32] } +func (h *headerSTAR) devMajor() []byte { return h[329:][:8] } +func (h *headerSTAR) devMinor() []byte { return h[337:][:8] } +func (h *headerSTAR) prefix() []byte { return h[345:][:131] } +func (h *headerSTAR) accessTime() []byte { return h[476:][:12] } +func (h *headerSTAR) changeTime() []byte { return h[488:][:12] } +func (h *headerSTAR) trailer() []byte { return h[508:][:4] } type headerUSTAR [blockSize]byte -func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) } -func (h *headerUSTAR) Magic() []byte { return h[257:][:6] } -func (h *headerUSTAR) Version() []byte { return h[263:][:2] } -func (h *headerUSTAR) UserName() []byte { return h[265:][:32] } -func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] } -func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] } -func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] } -func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] } +func (h *headerUSTAR) v7() *headerV7 { return (*headerV7)(h) } +func (h *headerUSTAR) magic() []byte { return h[257:][:6] } +func (h *headerUSTAR) version() []byte { return h[263:][:2] } +func (h *headerUSTAR) userName() []byte { return h[265:][:32] } +func (h *headerUSTAR) groupName() []byte { return h[297:][:32] } +func (h *headerUSTAR) devMajor() []byte { return h[329:][:8] } +func (h *headerUSTAR) devMinor() []byte { return h[337:][:8] } +func (h *headerUSTAR) prefix() []byte { return h[345:][:155] } type sparseArray []byte -func (s sparseArray) Entry(i int) sparseElem { return sparseElem(s[i*24:]) } -func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } -func (s sparseArray) MaxEntries() int { return len(s) / 24 } +func (s sparseArray) entry(i int) sparseElem { return sparseElem(s[i*24:]) } +func (s sparseArray) isExtended() []byte { return s[24*s.maxEntries():][:1] } +func (s sparseArray) maxEntries() int { return len(s) / 24 } type sparseElem []byte -func (s sparseElem) Offset() []byte { return s[00:][:12] } -func (s sparseElem) Length() []byte { return s[12:][:12] } +func (s sparseElem) offset() []byte { return s[00:][:12] } +func (s sparseElem) length() []byte { return s[12:][:12] } diff --git a/libgo/go/archive/tar/fuzz_test.go b/libgo/go/archive/tar/fuzz_test.go new file mode 100644 index 0000000..e73e0d2 --- /dev/null +++ b/libgo/go/archive/tar/fuzz_test.go @@ -0,0 +1,80 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "io" + "testing" +) + +func FuzzReader(f *testing.F) { + b := bytes.NewBuffer(nil) + w := NewWriter(b) + inp := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") + err := w.WriteHeader(&Header{ + Name: "lorem.txt", + Mode: 0600, + Size: int64(len(inp)), + }) + if err != nil { + f.Fatalf("failed to create writer: %s", err) + } + _, err = w.Write(inp) + if err != nil { + f.Fatalf("failed to write file to archive: %s", err) + } + if err := w.Close(); err != nil { + f.Fatalf("failed to write archive: %s", err) + } + f.Add(b.Bytes()) + + f.Fuzz(func(t *testing.T, b []byte) { + r := NewReader(bytes.NewReader(b)) + type file struct { + header *Header + content []byte + } + files := []file{} + for { + hdr, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return + } + buf := bytes.NewBuffer(nil) + if _, err := io.Copy(buf, r); err != nil { + continue + } + files = append(files, file{header: hdr, content: buf.Bytes()}) + } + + // If we were unable to read anything out of the archive don't + // bother trying to roundtrip it. + if len(files) == 0 { + return + } + + out := bytes.NewBuffer(nil) + w := NewWriter(out) + for _, f := range files { + if err := w.WriteHeader(f.header); err != nil { + t.Fatalf("unable to write previously parsed header: %s", err) + } + if _, err := w.Write(f.content); err != nil { + t.Fatalf("unable to write previously parsed content: %s", err) + } + } + if err := w.Close(); err != nil { + t.Fatalf("Unable to write archive: %s", err) + } + + // TODO: We may want to check if the archive roundtrips. This would require + // taking into account addition of the two zero trailer blocks that Writer.Close + // appends. + }) +} diff --git a/libgo/go/archive/tar/reader.go b/libgo/go/archive/tar/reader.go index 1b1d5b4..4b11909 100644 --- a/libgo/go/archive/tar/reader.go +++ b/libgo/go/archive/tar/reader.go @@ -65,7 +65,7 @@ func (tr *Reader) next() (*Header, error) { format := FormatUSTAR | FormatPAX | FormatGNU for { // Discard the remainder of the file and any padding. - if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil { + if err := discard(tr.r, tr.curr.physicalRemaining()); err != nil { return nil, err } if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil { @@ -355,7 +355,7 @@ func (tr *Reader) readHeader() (*Header, *block, error) { } // Verify the header matches a known format. - format := tr.blk.GetFormat() + format := tr.blk.getFormat() if format == FormatUnknown { return nil, nil, ErrHeader } @@ -364,30 +364,30 @@ func (tr *Reader) readHeader() (*Header, *block, error) { hdr := new(Header) // Unpack the V7 header. - v7 := tr.blk.V7() - hdr.Typeflag = v7.TypeFlag()[0] - hdr.Name = p.parseString(v7.Name()) - hdr.Linkname = p.parseString(v7.LinkName()) - hdr.Size = p.parseNumeric(v7.Size()) - hdr.Mode = p.parseNumeric(v7.Mode()) - hdr.Uid = int(p.parseNumeric(v7.UID())) - hdr.Gid = int(p.parseNumeric(v7.GID())) - hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) + v7 := tr.blk.toV7() + hdr.Typeflag = v7.typeFlag()[0] + hdr.Name = p.parseString(v7.name()) + hdr.Linkname = p.parseString(v7.linkName()) + hdr.Size = p.parseNumeric(v7.size()) + hdr.Mode = p.parseNumeric(v7.mode()) + hdr.Uid = int(p.parseNumeric(v7.uid())) + hdr.Gid = int(p.parseNumeric(v7.gid())) + hdr.ModTime = time.Unix(p.parseNumeric(v7.modTime()), 0) // Unpack format specific fields. if format > formatV7 { - ustar := tr.blk.USTAR() - hdr.Uname = p.parseString(ustar.UserName()) - hdr.Gname = p.parseString(ustar.GroupName()) - hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) - hdr.Devminor = p.parseNumeric(ustar.DevMinor()) + ustar := tr.blk.toUSTAR() + hdr.Uname = p.parseString(ustar.userName()) + hdr.Gname = p.parseString(ustar.groupName()) + hdr.Devmajor = p.parseNumeric(ustar.devMajor()) + hdr.Devminor = p.parseNumeric(ustar.devMinor()) var prefix string switch { case format.has(FormatUSTAR | FormatPAX): hdr.Format = format - ustar := tr.blk.USTAR() - prefix = p.parseString(ustar.Prefix()) + ustar := tr.blk.toUSTAR() + prefix = p.parseString(ustar.prefix()) // For Format detection, check if block is properly formatted since // the parser is more liberal than what USTAR actually permits. @@ -396,23 +396,23 @@ func (tr *Reader) readHeader() (*Header, *block, error) { hdr.Format = FormatUnknown // Non-ASCII characters in block. } nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } - if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) && - nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) { + if !(nul(v7.size()) && nul(v7.mode()) && nul(v7.uid()) && nul(v7.gid()) && + nul(v7.modTime()) && nul(ustar.devMajor()) && nul(ustar.devMinor())) { hdr.Format = FormatUnknown // Numeric fields must end in NUL } case format.has(formatSTAR): - star := tr.blk.STAR() - prefix = p.parseString(star.Prefix()) - hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0) - hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0) + star := tr.blk.toSTAR() + prefix = p.parseString(star.prefix()) + hdr.AccessTime = time.Unix(p.parseNumeric(star.accessTime()), 0) + hdr.ChangeTime = time.Unix(p.parseNumeric(star.changeTime()), 0) case format.has(FormatGNU): hdr.Format = format var p2 parser - gnu := tr.blk.GNU() - if b := gnu.AccessTime(); b[0] != 0 { + gnu := tr.blk.toGNU() + if b := gnu.accessTime(); b[0] != 0 { hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0) } - if b := gnu.ChangeTime(); b[0] != 0 { + if b := gnu.changeTime(); b[0] != 0 { hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0) } @@ -439,8 +439,8 @@ func (tr *Reader) readHeader() (*Header, *block, error) { // 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) { + ustar := tr.blk.toUSTAR() + if s := p.parseString(ustar.prefix()); isASCII(s) { prefix = s } hdr.Format = FormatUnknown // Buggy file is not GNU @@ -465,38 +465,38 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err // Make sure that the input format is GNU. // Unfortunately, the STAR format also has a sparse header format that uses // the same type flag but has a completely different layout. - if blk.GetFormat() != FormatGNU { + if blk.getFormat() != FormatGNU { return nil, ErrHeader } hdr.Format.mayOnlyBe(FormatGNU) var p parser - hdr.Size = p.parseNumeric(blk.GNU().RealSize()) + hdr.Size = p.parseNumeric(blk.toGNU().realSize()) if p.err != nil { return nil, p.err } - s := blk.GNU().Sparse() - spd := make(sparseDatas, 0, s.MaxEntries()) + s := blk.toGNU().sparse() + spd := make(sparseDatas, 0, s.maxEntries()) for { - for i := 0; i < s.MaxEntries(); i++ { + for i := 0; i < s.maxEntries(); i++ { // This termination condition is identical to GNU and BSD tar. - if s.Entry(i).Offset()[0] == 0x00 { + if s.entry(i).offset()[0] == 0x00 { break // Don't return, need to process extended headers (even if empty) } - offset := p.parseNumeric(s.Entry(i).Offset()) - length := p.parseNumeric(s.Entry(i).Length()) + offset := p.parseNumeric(s.entry(i).offset()) + length := p.parseNumeric(s.entry(i).length()) if p.err != nil { return nil, p.err } spd = append(spd, sparseEntry{Offset: offset, Length: length}) } - if s.IsExtended()[0] > 0 { + if s.isExtended()[0] > 0 { // There are more entries. Read an extension header and parse its entries. if _, err := mustReadFull(tr.r, blk[:]); err != nil { return nil, err } - s = blk.Sparse() + s = blk.toSparse() continue } return spd, nil // Done @@ -678,11 +678,13 @@ func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) { return io.Copy(w, struct{ io.Reader }{fr}) } -func (fr regFileReader) LogicalRemaining() int64 { +// logicalRemaining implements fileState.logicalRemaining. +func (fr regFileReader) logicalRemaining() int64 { return fr.nb } -func (fr regFileReader) PhysicalRemaining() int64 { +// logicalRemaining implements fileState.physicalRemaining. +func (fr regFileReader) physicalRemaining() int64 { return fr.nb } @@ -694,9 +696,9 @@ type sparseFileReader struct { } func (sr *sparseFileReader) Read(b []byte) (n int, err error) { - finished := int64(len(b)) >= sr.LogicalRemaining() + finished := int64(len(b)) >= sr.logicalRemaining() if finished { - b = b[:sr.LogicalRemaining()] + b = b[:sr.logicalRemaining()] } b0 := b @@ -724,7 +726,7 @@ func (sr *sparseFileReader) Read(b []byte) (n int, err error) { return n, errMissData // Less data in dense file than sparse file case err != nil: return n, err - case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0: return n, errUnrefData // More data in dense file than sparse file case finished: return n, io.EOF @@ -746,7 +748,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { var writeLastByte bool pos0 := sr.pos - for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil { + for sr.logicalRemaining() > 0 && !writeLastByte && err == nil { var nf int64 // Size of fragment holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() if sr.pos < holeStart { // In a data fragment @@ -754,7 +756,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { nf, err = io.CopyN(ws, sr.fr, nf) } else { // In a hole fragment nf = holeEnd - sr.pos - if sr.PhysicalRemaining() == 0 { + if sr.physicalRemaining() == 0 { writeLastByte = true nf-- } @@ -779,18 +781,18 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { return n, errMissData // Less data in dense file than sparse file case err != nil: return n, err - case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0: return n, errUnrefData // More data in dense file than sparse file default: return n, nil } } -func (sr sparseFileReader) LogicalRemaining() int64 { +func (sr sparseFileReader) logicalRemaining() int64 { return sr.sp[len(sr.sp)-1].endOffset() - sr.pos } -func (sr sparseFileReader) PhysicalRemaining() int64 { - return sr.fr.PhysicalRemaining() +func (sr sparseFileReader) physicalRemaining() int64 { + return sr.fr.physicalRemaining() } type zeroReader struct{} diff --git a/libgo/go/archive/tar/reader_test.go b/libgo/go/archive/tar/reader_test.go index 789ddc1..f21a606 100644 --- a/libgo/go/archive/tar/reader_test.go +++ b/libgo/go/archive/tar/reader_test.go @@ -1021,12 +1021,12 @@ func TestParsePAX(t *testing.T) { func TestReadOldGNUSparseMap(t *testing.T) { populateSparseMap := func(sa sparseArray, sps []string) []string { - for i := 0; len(sps) > 0 && i < sa.MaxEntries(); i++ { - copy(sa.Entry(i), sps[0]) + for i := 0; len(sps) > 0 && i < sa.maxEntries(); i++ { + copy(sa.entry(i), sps[0]) sps = sps[1:] } if len(sps) > 0 { - copy(sa.IsExtended(), "\x80") + copy(sa.isExtended(), "\x80") } return sps } @@ -1034,19 +1034,19 @@ func TestReadOldGNUSparseMap(t *testing.T) { makeInput := func(format Format, size string, sps ...string) (out []byte) { // Write the initial GNU header. var blk block - gnu := blk.GNU() - sparse := gnu.Sparse() - copy(gnu.RealSize(), size) + gnu := blk.toGNU() + sparse := gnu.sparse() + copy(gnu.realSize(), size) sps = populateSparseMap(sparse, sps) if format != FormatUnknown { - blk.SetFormat(format) + blk.setFormat(format) } out = append(out, blk[:]...) // Write extended sparse blocks. for len(sps) > 0 { var blk block - sps = populateSparseMap(blk.Sparse(), sps) + sps = populateSparseMap(blk.toSparse(), sps) out = append(out, blk[:]...) } return out @@ -1359,11 +1359,11 @@ func TestFileReader(t *testing.T) { wantCnt int64 wantErr error } - testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt + testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt wantLCnt int64 wantPCnt int64 } - testFnc interface{} // testRead | testWriteTo | testRemaining + testFnc any // testRead | testWriteTo | testRemaining ) type ( @@ -1376,7 +1376,7 @@ func TestFileReader(t *testing.T) { spd sparseDatas size int64 } - fileMaker interface{} // makeReg | makeSparse + fileMaker any // makeReg | makeSparse ) vectors := []struct { @@ -1596,11 +1596,11 @@ func TestFileReader(t *testing.T) { t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) } case testRemaining: - if got := fr.LogicalRemaining(); got != tf.wantLCnt { - t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) + if got := fr.logicalRemaining(); got != tf.wantLCnt { + t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) } - if got := fr.PhysicalRemaining(); got != tf.wantPCnt { - t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) + if got := fr.physicalRemaining(); got != tf.wantPCnt { + t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) } default: t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) diff --git a/libgo/go/archive/tar/stat_actime1.go b/libgo/go/archive/tar/stat_actime1.go index 71cece5..5e4a00d 100644 --- a/libgo/go/archive/tar/stat_actime1.go +++ b/libgo/go/archive/tar/stat_actime1.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || hurd || linux || dragonfly || openbsd || solaris -// +build aix hurd linux dragonfly openbsd solaris package tar diff --git a/libgo/go/archive/tar/stat_actime2.go b/libgo/go/archive/tar/stat_actime2.go index 5a9a35c..f76d6be 100644 --- a/libgo/go/archive/tar/stat_actime2.go +++ b/libgo/go/archive/tar/stat_actime2.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || netbsd -// +build darwin freebsd netbsd package tar diff --git a/libgo/go/archive/tar/stat_unix.go b/libgo/go/archive/tar/stat_unix.go index f27df67..1349f3e 100644 --- a/libgo/go/archive/tar/stat_unix.go +++ b/libgo/go/archive/tar/stat_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || hurd || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris -// +build aix hurd linux darwin dragonfly freebsd openbsd netbsd solaris package tar diff --git a/libgo/go/archive/tar/strconv.go b/libgo/go/archive/tar/strconv.go index f0b61e6..275db6f 100644 --- a/libgo/go/archive/tar/strconv.go +++ b/libgo/go/archive/tar/strconv.go @@ -14,7 +14,7 @@ import ( // hasNUL reports whether the NUL character exists within s. func hasNUL(s string) bool { - return strings.IndexByte(s, 0) >= 0 + return strings.Contains(s, "\x00") } // isASCII reports whether the input is an ASCII C-style string. @@ -201,10 +201,7 @@ func parsePAXTime(s string) (time.Time, error) { const maxNanoSecondDigits = 9 // Split string into seconds and sub-seconds parts. - ss, sn := s, "" - if pos := strings.IndexByte(s, '.'); pos >= 0 { - ss, sn = s[:pos], s[pos+1:] - } + ss, sn, _ := strings.Cut(s, ".") // Parse the seconds. secs, err := strconv.ParseInt(ss, 10, 64) @@ -254,48 +251,32 @@ func formatPAXTime(ts time.Time) (s string) { // return the remainder as r. func parsePAXRecord(s string) (k, v, r string, err error) { // The size field ends at the first space. - sp := strings.IndexByte(s, ' ') - if sp == -1 { + nStr, rest, ok := strings.Cut(s, " ") + if !ok { return "", "", s, ErrHeader } // Parse the first token as a decimal integer. - n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int - if perr != nil || n < 5 || int64(len(s)) < n { + n, perr := strconv.ParseInt(nStr, 10, 0) // Intentionally parse as native int + if perr != nil || n < 5 || n > int64(len(s)) { return "", "", s, ErrHeader } - - afterSpace := int64(sp + 1) - beforeLastNewLine := n - 1 - // In some cases, "length" was perhaps padded/malformed, and - // trying to index past where the space supposedly is goes past - // the end of the actual record. - // For example: - // "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319" - // ^ ^ - // | | - // | afterSpace=35 - // | - // beforeLastNewLine=29 - // yet indexOf(firstSpace) MUST BE before endOfRecord. - // - // See https://golang.org/issues/40196. - if afterSpace >= beforeLastNewLine { + n -= int64(len(nStr) + 1) // convert from index in s to index in rest + if n <= 0 { return "", "", s, ErrHeader } // Extract everything between the space and the final newline. - rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:] + rec, nl, rem := rest[:n-1], rest[n-1:n], rest[n:] if nl != "\n" { return "", "", s, ErrHeader } // The first equals separates the key from the value. - eq := strings.IndexByte(rec, '=') - if eq == -1 { + k, v, ok = strings.Cut(rec, "=") + if !ok { return "", "", s, ErrHeader } - k, v = rec[:eq], rec[eq+1:] if !validPAXRecord(k, v) { return "", "", s, ErrHeader @@ -333,7 +314,7 @@ func formatPAXRecord(k, v string) (string, error) { // for the PAX version of the USTAR string fields. // The key must not contain an '=' character. func validPAXRecord(k, v string) bool { - if k == "" || strings.IndexByte(k, '=') >= 0 { + if k == "" || strings.Contains(k, "=") { return false } switch k { diff --git a/libgo/go/archive/tar/tar_test.go b/libgo/go/archive/tar/tar_test.go index e9fafc7..a476f5e 100644 --- a/libgo/go/archive/tar/tar_test.go +++ b/libgo/go/archive/tar/tar_test.go @@ -23,7 +23,7 @@ import ( type testError struct{ error } -type fileOps []interface{} // []T where T is (string | int64) +type fileOps []any // []T where T is (string | int64) // testFile is an io.ReadWriteSeeker where the IO operations performed // on it must match the list of operations in ops. diff --git a/libgo/go/archive/tar/writer.go b/libgo/go/archive/tar/writer.go index e80498d..3729f7e 100644 --- a/libgo/go/archive/tar/writer.go +++ b/libgo/go/archive/tar/writer.go @@ -50,7 +50,7 @@ func (tw *Writer) Flush() error { if tw.err != nil { return tw.err } - if nb := tw.curr.LogicalRemaining(); nb > 0 { + if nb := tw.curr.logicalRemaining(); nb > 0 { return fmt.Errorf("archive/tar: missed writing %d bytes", nb) } if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { @@ -117,8 +117,8 @@ func (tw *Writer) writeUSTARHeader(hdr *Header) error { // Pack the main header. var f formatter blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) - f.formatString(blk.USTAR().Prefix(), namePrefix) - blk.SetFormat(FormatUSTAR) + f.formatString(blk.toUSTAR().prefix(), namePrefix) + blk.setFormat(FormatUSTAR) if f.err != nil { return f.err // Should never happen since header is validated } @@ -208,7 +208,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { var f formatter // Ignore errors since they are expected fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) - blk.SetFormat(FormatPAX) + blk.setFormat(FormatPAX) if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { return err } @@ -250,10 +250,10 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error { var spb []byte blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) if !hdr.AccessTime.IsZero() { - f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) + f.formatNumeric(blk.toGNU().accessTime(), hdr.AccessTime.Unix()) } if !hdr.ChangeTime.IsZero() { - f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) + f.formatNumeric(blk.toGNU().changeTime(), hdr.ChangeTime.Unix()) } // TODO(dsnet): Re-enable this when adding sparse support. // See https://golang.org/issue/22735 @@ -293,7 +293,7 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error { f.formatNumeric(blk.GNU().RealSize(), realSize) } */ - blk.SetFormat(FormatGNU) + blk.setFormat(FormatGNU) if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { return err } @@ -321,28 +321,28 @@ type ( // The block returned is only valid until the next call to // templateV7Plus or writeRawFile. func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { - tw.blk.Reset() + tw.blk.reset() modTime := hdr.ModTime if modTime.IsZero() { modTime = time.Unix(0, 0) } - v7 := tw.blk.V7() - v7.TypeFlag()[0] = hdr.Typeflag - fmtStr(v7.Name(), hdr.Name) - fmtStr(v7.LinkName(), hdr.Linkname) - fmtNum(v7.Mode(), hdr.Mode) - fmtNum(v7.UID(), int64(hdr.Uid)) - fmtNum(v7.GID(), int64(hdr.Gid)) - fmtNum(v7.Size(), hdr.Size) - fmtNum(v7.ModTime(), modTime.Unix()) + v7 := tw.blk.toV7() + v7.typeFlag()[0] = hdr.Typeflag + fmtStr(v7.name(), hdr.Name) + fmtStr(v7.linkName(), hdr.Linkname) + fmtNum(v7.mode(), hdr.Mode) + fmtNum(v7.uid(), int64(hdr.Uid)) + fmtNum(v7.gid(), int64(hdr.Gid)) + fmtNum(v7.size(), hdr.Size) + fmtNum(v7.modTime(), modTime.Unix()) - ustar := tw.blk.USTAR() - fmtStr(ustar.UserName(), hdr.Uname) - fmtStr(ustar.GroupName(), hdr.Gname) - fmtNum(ustar.DevMajor(), hdr.Devmajor) - fmtNum(ustar.DevMinor(), hdr.Devminor) + ustar := tw.blk.toUSTAR() + fmtStr(ustar.userName(), hdr.Uname) + fmtStr(ustar.groupName(), hdr.Gname) + fmtNum(ustar.devMajor(), hdr.Devmajor) + fmtNum(ustar.devMinor(), hdr.Devminor) return &tw.blk } @@ -351,7 +351,7 @@ func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum num // It uses format to encode the header format and will write data as the body. // It uses default values for all of the other fields (as BSD and GNU tar does). func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { - tw.blk.Reset() + tw.blk.reset() // Best effort for the filename. name = toASCII(name) @@ -361,15 +361,15 @@ func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) erro name = strings.TrimRight(name, "/") var f formatter - v7 := tw.blk.V7() - v7.TypeFlag()[0] = flag - f.formatString(v7.Name(), name) - f.formatOctal(v7.Mode(), 0) - f.formatOctal(v7.UID(), 0) - f.formatOctal(v7.GID(), 0) - f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB - f.formatOctal(v7.ModTime(), 0) - tw.blk.SetFormat(format) + v7 := tw.blk.toV7() + v7.typeFlag()[0] = flag + f.formatString(v7.name(), name) + f.formatOctal(v7.mode(), 0) + f.formatOctal(v7.uid(), 0) + f.formatOctal(v7.gid(), 0) + f.formatOctal(v7.size(), int64(len(data))) // Must be < 8GiB + f.formatOctal(v7.modTime(), 0) + tw.blk.setFormat(format) if f.err != nil { return f.err // Only occurs if size condition is violated } @@ -511,10 +511,13 @@ func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) { return io.Copy(struct{ io.Writer }{fw}, r) } -func (fw regFileWriter) LogicalRemaining() int64 { +// logicalRemaining implements fileState.logicalRemaining. +func (fw regFileWriter) logicalRemaining() int64 { return fw.nb } -func (fw regFileWriter) PhysicalRemaining() int64 { + +// logicalRemaining implements fileState.physicalRemaining. +func (fw regFileWriter) physicalRemaining() int64 { return fw.nb } @@ -526,9 +529,9 @@ type sparseFileWriter struct { } func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { - overwrite := int64(len(b)) > sw.LogicalRemaining() + overwrite := int64(len(b)) > sw.logicalRemaining() if overwrite { - b = b[:sw.LogicalRemaining()] + b = b[:sw.logicalRemaining()] } b0 := b @@ -556,7 +559,7 @@ func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { return n, errMissData // Not possible; implies bug in validation logic case err != nil: return n, err - case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0: return n, errUnrefData // Not possible; implies bug in validation logic case overwrite: return n, ErrWriteTooLong @@ -578,12 +581,12 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { var readLastByte bool pos0 := sw.pos - for sw.LogicalRemaining() > 0 && !readLastByte && err == nil { + for sw.logicalRemaining() > 0 && !readLastByte && err == nil { var nf int64 // Size of fragment dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() if sw.pos < dataStart { // In a hole fragment nf = dataStart - sw.pos - if sw.PhysicalRemaining() == 0 { + if sw.physicalRemaining() == 0 { readLastByte = true nf-- } @@ -613,18 +616,18 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { return n, errMissData // Not possible; implies bug in validation logic case err != nil: return n, err - case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0: return n, errUnrefData // Not possible; implies bug in validation logic default: return n, ensureEOF(rs) } } -func (sw sparseFileWriter) LogicalRemaining() int64 { +func (sw sparseFileWriter) logicalRemaining() int64 { return sw.sp[len(sw.sp)-1].endOffset() - sw.pos } -func (sw sparseFileWriter) PhysicalRemaining() int64 { - return sw.fw.PhysicalRemaining() +func (sw sparseFileWriter) physicalRemaining() int64 { + return sw.fw.physicalRemaining() } // zeroWriter may only be written with NULs, otherwise it returns errWriteHole. diff --git a/libgo/go/archive/tar/writer_test.go b/libgo/go/archive/tar/writer_test.go index a00f02d..da3fb89 100644 --- a/libgo/go/archive/tar/writer_test.go +++ b/libgo/go/archive/tar/writer_test.go @@ -67,7 +67,7 @@ func TestWriter(t *testing.T) { testClose struct { // Close() == wantErr wantErr error } - testFnc interface{} // testHeader | testWrite | testReadFrom | testClose + testFnc any // testHeader | testWrite | testReadFrom | testClose ) vectors := []struct { @@ -987,11 +987,9 @@ func TestIssue12594(t *testing.T) { // The prefix field should never appear in the GNU format. var blk block copy(blk[:], b.Bytes()) - prefix := string(blk.USTAR().Prefix()) - if i := strings.IndexByte(prefix, 0); i >= 0 { - prefix = prefix[:i] // Truncate at the NUL terminator - } - if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { + prefix := string(blk.toUSTAR().prefix()) + prefix, _, _ = strings.Cut(prefix, "\x00") // Truncate at the NUL terminator + if blk.getFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { t.Errorf("test %d, found prefix in GNU format: %s", i, prefix) } @@ -1029,11 +1027,11 @@ func TestFileWriter(t *testing.T) { wantCnt int64 wantErr error } - testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt + testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt wantLCnt int64 wantPCnt int64 } - testFnc interface{} // testWrite | testReadFrom | testRemaining + testFnc any // testWrite | testReadFrom | testRemaining ) type ( @@ -1046,7 +1044,7 @@ func TestFileWriter(t *testing.T) { sph sparseHoles size int64 } - fileMaker interface{} // makeReg | makeSparse + fileMaker any // makeReg | makeSparse ) vectors := []struct { @@ -1292,11 +1290,11 @@ func TestFileWriter(t *testing.T) { t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) } case testRemaining: - if got := fw.LogicalRemaining(); got != tf.wantLCnt { - t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) + if got := fw.logicalRemaining(); got != tf.wantLCnt { + t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) } - if got := fw.PhysicalRemaining(); got != tf.wantPCnt { - t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) + if got := fw.physicalRemaining(); got != tf.wantPCnt { + t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) } default: t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) diff --git a/libgo/go/archive/zip/fuzz_test.go b/libgo/go/archive/zip/fuzz_test.go new file mode 100644 index 0000000..7dffde6 --- /dev/null +++ b/libgo/go/archive/zip/fuzz_test.go @@ -0,0 +1,81 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "bytes" + "io" + "os" + "path/filepath" + "testing" +) + +func FuzzReader(f *testing.F) { + testdata, err := os.ReadDir("testdata") + if err != nil { + f.Fatalf("failed to read testdata directory: %s", err) + } + for _, de := range testdata { + if de.IsDir() { + continue + } + b, err := os.ReadFile(filepath.Join("testdata", de.Name())) + if err != nil { + f.Fatalf("failed to read testdata: %s", err) + } + f.Add(b) + } + + f.Fuzz(func(t *testing.T, b []byte) { + r, err := NewReader(bytes.NewReader(b), int64(len(b))) + if err != nil { + return + } + + type file struct { + header *FileHeader + content []byte + } + files := []file{} + + for _, f := range r.File { + fr, err := f.Open() + if err != nil { + continue + } + content, err := io.ReadAll(fr) + if err != nil { + continue + } + files = append(files, file{header: &f.FileHeader, content: content}) + if _, err := r.Open(f.Name); err != nil { + continue + } + } + + // If we were unable to read anything out of the archive don't + // bother trying to roundtrip it. + if len(files) == 0 { + return + } + + w := NewWriter(io.Discard) + for _, f := range files { + ww, err := w.CreateHeader(f.header) + if err != nil { + t.Fatalf("unable to write previously parsed header: %s", err) + } + if _, err := ww.Write(f.content); err != nil { + t.Fatalf("unable to write previously parsed content: %s", err) + } + } + + if err := w.Close(); err != nil { + t.Fatalf("Unable to write archive: %s", err) + } + + // TODO: We may want to check if the archive roundtrips. + }) +} diff --git a/libgo/go/archive/zip/reader.go b/libgo/go/archive/zip/reader.go index c91a8d0..92fd6f6 100644 --- a/libgo/go/archive/zip/reader.go +++ b/libgo/go/archive/zip/reader.go @@ -125,7 +125,6 @@ func (z *Reader) init(r io.ReaderAt, size int64) error { if err != nil { return err } - f.readDataDescriptor() z.File = append(z.File, f) } if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here @@ -186,10 +185,15 @@ func (f *File) Open() (io.ReadCloser, error) { return nil, ErrAlgorithm } var rc io.ReadCloser = dcomp(r) + var desr io.Reader + if f.hasDataDescriptor() { + desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) + } rc = &checksumReader{ rc: rc, hash: crc32.NewIEEE(), f: f, + desr: desr, } return rc, nil } @@ -205,49 +209,13 @@ func (f *File) OpenRaw() (io.Reader, error) { return r, nil } -func (f *File) readDataDescriptor() { - if !f.hasDataDescriptor() { - return - } - - bodyOffset, err := f.findBodyOffset() - if err != nil { - f.descErr = err - return - } - - // In section 4.3.9.2 of the spec: "However ZIP64 format MAY be used - // regardless of the size of a file. When extracting, if the zip64 - // extended information extra field is present for the file the - // compressed and uncompressed sizes will be 8 byte values." - // - // Historically, this package has used the compressed and uncompressed - // sizes from the central directory to determine if the package is - // zip64. - // - // For this case we allow either the extra field or sizes to determine - // the data descriptor length. - zip64 := f.zip64 || f.isZip64() - n := int64(dataDescriptorLen) - if zip64 { - n = dataDescriptor64Len - } - size := int64(f.CompressedSize64) - r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, n) - dd, err := readDataDescriptor(r, zip64) - if err != nil { - f.descErr = err - return - } - f.CRC32 = dd.crc32 -} - type checksumReader struct { rc io.ReadCloser hash hash.Hash32 nread uint64 // number of bytes read so far f *File - err error // sticky error + desr io.Reader // if non-nil, where to read the data descriptor + err error // sticky error } func (r *checksumReader) Stat() (fs.FileInfo, error) { @@ -268,12 +236,12 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { if r.nread != r.f.UncompressedSize64 { return 0, io.ErrUnexpectedEOF } - if r.f.hasDataDescriptor() { - if r.f.descErr != nil { - if r.f.descErr == io.EOF { + if r.desr != nil { + if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { + if err1 == io.EOF { err = io.ErrUnexpectedEOF } else { - err = r.f.descErr + err = err1 } } else if r.hash.Sum32() != r.f.CRC32 { err = ErrChecksum @@ -485,10 +453,8 @@ parseExtras: return nil } -func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) { - // Create enough space for the largest possible size - var buf [dataDescriptor64Len]byte - +func readDataDescriptor(r io.Reader, f *File) error { + var buf [dataDescriptorLen]byte // The spec says: "Although not originally assigned a // signature, the value 0x08074b50 has commonly been adopted // as a signature value for the data descriptor record. @@ -497,9 +463,10 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) { // descriptors and should account for either case when reading // ZIP files to ensure compatibility." // - // First read just those 4 bytes to see if the signature exists. + // dataDescriptorLen includes the size of the signature but + // first read just those 4 bytes to see if it exists. if _, err := io.ReadFull(r, buf[:4]); err != nil { - return nil, err + return err } off := 0 maybeSig := readBuf(buf[:4]) @@ -508,28 +475,21 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) { // bytes. off += 4 } - - end := dataDescriptorLen - 4 - if zip64 { - end = dataDescriptor64Len - 4 + if _, err := io.ReadFull(r, buf[off:12]); err != nil { + return err } - if _, err := io.ReadFull(r, buf[off:end]); err != nil { - return nil, err + b := readBuf(buf[:12]) + if b.uint32() != f.CRC32 { + return ErrChecksum } - b := readBuf(buf[:end]) - out := &dataDescriptor{ - crc32: b.uint32(), - } + // The two sizes that follow here can be either 32 bits or 64 bits + // but the spec is not very clear on this and different + // interpretations has been made causing incompatibilities. We + // already have the sizes from the central directory so we can + // just ignore these. - if zip64 { - out.compressedSize = b.uint64() - out.uncompressedSize = b.uint64() - } else { - out.compressedSize = uint64(b.uint32()) - out.uncompressedSize = uint64(b.uint32()) - } - return out, nil + return nil } func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { @@ -710,7 +670,7 @@ func (f *fileListEntry) Size() int64 { return 0 } func (f *fileListEntry) Mode() fs.FileMode { return fs.ModeDir | 0555 } func (f *fileListEntry) Type() fs.FileMode { return fs.ModeDir } func (f *fileListEntry) IsDir() bool { return true } -func (f *fileListEntry) Sys() interface{} { return nil } +func (f *fileListEntry) Sys() any { return nil } func (f *fileListEntry) ModTime() time.Time { if f.file == nil { @@ -741,6 +701,9 @@ func (r *Reader) initFileList() { for _, file := range r.File { isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/' name := toValidName(file.Name) + if name == "" { + continue + } for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) { dirs[dir] = true } @@ -782,8 +745,11 @@ func fileEntryLess(x, y string) bool { func (r *Reader) Open(name string) (fs.File, error) { r.initFileList() + if !fs.ValidPath(name) { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid} + } e := r.openLookup(name) - if e == nil || !fs.ValidPath(name) { + if e == nil { return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} } if e.isDir { @@ -797,7 +763,7 @@ func (r *Reader) Open(name string) (fs.File, error) { } func split(name string) (dir, elem string, isDir bool) { - if name[len(name)-1] == '/' { + if len(name) > 0 && name[len(name)-1] == '/' { isDir = true name = name[:len(name)-1] } diff --git a/libgo/go/archive/zip/reader_test.go b/libgo/go/archive/zip/reader_test.go index afb03ac..d1a9bdd 100644 --- a/libgo/go/archive/zip/reader_test.go +++ b/libgo/go/archive/zip/reader_test.go @@ -13,6 +13,7 @@ import ( "io/fs" "os" "path/filepath" + "reflect" "regexp" "strings" "testing" @@ -1202,127 +1203,14 @@ func TestCVE202127919(t *testing.T) { if err != nil { t.Errorf("Error reading file: %v", err) } -} - -func TestReadDataDescriptor(t *testing.T) { - tests := []struct { - desc string - in []byte - zip64 bool - want *dataDescriptor - wantErr error - }{{ - desc: "valid 32 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, // compressed size - 0x08, 0x09, 0x0a, 0x0b, // uncompressed size - }, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x07060504, - uncompressedSize: 0x0b0a0908, - }, - }, { - desc: "valid 32 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, // compressed size - 0x08, 0x09, 0x0a, 0x0b, // uncompressed size - }, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x07060504, - uncompressedSize: 0x0b0a0908, - }, - }, { - desc: "valid 64 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size - }, - zip64: true, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x0b0a090807060504, - uncompressedSize: 0x131211100f0e0d0c, - }, - }, { - desc: "valid 64 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size - }, - zip64: true, - want: &dataDescriptor{ - crc32: 0x03020100, - compressedSize: 0x0b0a090807060504, - uncompressedSize: 0x131211100f0e0d0c, - }, - }, { - desc: "invalid 32 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, // unexpected end - }, - wantErr: io.ErrUnexpectedEOF, - }, { - desc: "invalid 32 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, // unexpected end - }, - wantErr: io.ErrUnexpectedEOF, - }, { - desc: "invalid 64 bit with signature", - in: []byte{ - 0x50, 0x4b, 0x07, 0x08, // signature - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end - }, - zip64: true, - wantErr: io.ErrUnexpectedEOF, - }, { - desc: "invalid 64 bit without signature", - in: []byte{ - 0x00, 0x01, 0x02, 0x03, // crc32 - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end - }, - zip64: true, - wantErr: io.ErrUnexpectedEOF, - }} - - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - r := bytes.NewReader(test.in) - - desc, err := readDataDescriptor(r, test.zip64) - if err != test.wantErr { - t.Fatalf("got err %v; want nil", err) - } - if test.want == nil { - return - } - if desc == nil { - t.Fatalf("got nil DataDescriptor; want non-nil") - } - if desc.crc32 != test.want.crc32 { - t.Errorf("got CRC32 %#x; want %#x", desc.crc32, test.want.crc32) - } - if desc.compressedSize != test.want.compressedSize { - t.Errorf("got CompressedSize %#x; want %#x", desc.compressedSize, test.want.compressedSize) - } - if desc.uncompressedSize != test.want.uncompressedSize { - t.Errorf("got UncompressedSize %#x; want %#x", desc.uncompressedSize, test.want.uncompressedSize) - } - }) + if len(r.File) != 1 { + t.Fatalf("No entries in the file list") + } + if r.File[0].Name != "../test.txt" { + t.Errorf("Unexpected entry name: %s", r.File[0].Name) + } + if _, err := r.File[0].Open(); err != nil { + t.Errorf("Error opening file: %v", err) } } @@ -1402,3 +1290,121 @@ func TestCVE202139293(t *testing.T) { t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat) } } + +func TestCVE202141772(t *testing.T) { + // Archive contains a file whose name is exclusively made up of '/', '\' + // characters, or "../", "..\" paths, which would previously cause a panic. + // + // Length Method Size Cmpr Date Time CRC-32 Name + // -------- ------ ------- ---- ---------- ----- -------- ---- + // 0 Stored 0 0% 08-05-2021 18:32 00000000 / + // 0 Stored 0 0% 09-14-2021 12:59 00000000 // + // 0 Stored 0 0% 09-14-2021 12:59 00000000 \ + // 11 Stored 11 0% 09-14-2021 13:04 0d4a1185 /test.txt + // -------- ------- --- ------- + // 11 11 0% 4 files + data := []byte{ + 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x06, 0x94, 0x05, 0x53, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2f, 0x50, + 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x50, + 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x50, 0x4b, + 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, 0x4a, 0x0d, + 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x74, 0x78, 0x74, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x0a, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x94, 0x05, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0xed, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x50, + 0x4b, 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x0a, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x18, 0x00, 0x93, 0x98, 0x25, 0x57, 0x25, + 0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25, + 0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25, + 0xa9, 0xd7, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x3f, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, + 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x00, 0x5c, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x93, 0x98, + 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98, + 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98, + 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x50, 0x4b, + 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, + 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, + 0x00, 0xa9, 0x80, 0x51, 0x01, 0x26, 0xa9, 0xd7, + 0x01, 0x31, 0xd1, 0x57, 0x01, 0x26, 0xa9, 0xd7, + 0x01, 0xdf, 0x48, 0x85, 0xf9, 0x25, 0xa9, 0xd7, + 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, 0x00, 0x31, 0x01, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + } + r, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data))) + if err != nil { + t.Fatalf("Error reading the archive: %v", err) + } + entryNames := []string{`/`, `//`, `\`, `/test.txt`} + var names []string + for _, f := range r.File { + names = append(names, f.Name) + if _, err := f.Open(); err != nil { + t.Errorf("Error opening %q: %v", f.Name, err) + } + if _, err := r.Open(f.Name); err == nil { + t.Errorf("Opening %q with fs.FS API succeeded", f.Name) + } + } + if !reflect.DeepEqual(names, entryNames) { + t.Errorf("Unexpected file entries: %q", names) + } + if _, err := r.Open(""); err == nil { + t.Errorf("Opening %q with fs.FS API succeeded", "") + } + if _, err := r.Open("test.txt"); err != nil { + t.Errorf("Error opening %q with fs.FS API: %v", "test.txt", err) + } + dirEntries, err := fs.ReadDir(r, ".") + if err != nil { + t.Fatalf("Error reading the root directory: %v", err) + } + if len(dirEntries) != 1 || dirEntries[0].Name() != "test.txt" { + t.Errorf("Unexpected directory entries") + for _, dirEntry := range dirEntries { + _, err := r.Open(dirEntry.Name()) + t.Logf("%q (Open error: %v)", dirEntry.Name(), err) + } + t.FailNow() + } + info, err := dirEntries[0].Info() + if err != nil { + t.Fatalf("Error reading info entry: %v", err) + } + if name := info.Name(); name != "test.txt" { + t.Errorf("Inconsistent name in info entry: %v", name) + } +} diff --git a/libgo/go/archive/zip/struct.go b/libgo/go/archive/zip/struct.go index ff9f605..6f73fb8 100644 --- a/libgo/go/archive/zip/struct.go +++ b/libgo/go/archive/zip/struct.go @@ -163,7 +163,7 @@ func (fi headerFileInfo) ModTime() time.Time { } func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() } func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() } -func (fi headerFileInfo) Sys() interface{} { return fi.fh } +func (fi headerFileInfo) Sys() any { return fi.fh } func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil } @@ -390,11 +390,3 @@ func unixModeToFileMode(m uint32) fs.FileMode { } return mode } - -// dataDescriptor holds the data descriptor that optionally follows the file -// contents in the zip file. -type dataDescriptor struct { - crc32 uint32 - compressedSize uint64 - uncompressedSize uint64 -} diff --git a/libgo/go/archive/zip/writer_test.go b/libgo/go/archive/zip/writer_test.go index 97c6c52..2b73eca 100644 --- a/libgo/go/archive/zip/writer_test.go +++ b/libgo/go/archive/zip/writer_test.go @@ -362,7 +362,7 @@ func TestWriterDirAttributes(t *testing.T) { } binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature)) - if bytes.Index(b, sig[:]) != -1 { + if bytes.Contains(b, sig[:]) { t.Error("there should be no data descriptor") } } |