From 06eea2c9b87c008e3ad7cf3bad8f347aaa342d6b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 7 May 2021 15:59:32 -0700 Subject: [PATCH] Store real number of records in regular end record when possible Only store uintmax for the number of entries in the regular end record if it doesn't fit. p7zip 16.02 rejects zip files where the number of entries in the regular end record is larger than the number of entries counted in the central directory. Fixes: 187485108 Test: TestZip64P7ZipRecords Change-Id: I0d116e228a0ee26e4e95bb3f35771da236a056eb --- third_party/zip/android_test.go | 80 +++++++++++++++++++++++++++++++++ third_party/zip/writer.go | 9 +++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/third_party/zip/android_test.go b/third_party/zip/android_test.go index 9932c1b13..46588d463 100644 --- a/third_party/zip/android_test.go +++ b/third_party/zip/android_test.go @@ -140,3 +140,83 @@ func TestCopyFromZip64(t *testing.T) { t.Errorf("Expected UnompressedSize64 %d, got %d", w, g) } } + +// Test for b/187485108: zip64 output can't be read by p7zip 16.02. +func TestZip64P7ZipRecords(t *testing.T) { + if testing.Short() { + t.Skip("slow test; skipping") + } + + const size = uint32max + 1 + zipBytes := &bytes.Buffer{} + zip := NewWriter(zipBytes) + f, err := zip.CreateHeaderAndroid(&FileHeader{ + Name: "large", + Method: Store, + UncompressedSize64: size, + CompressedSize64: size, + }) + if err != nil { + t.Fatalf("Create: %v", err) + } + _, err = f.Write(make([]byte, size)) + if err != nil { + t.Fatalf("Write: %v", err) + } + err = zip.Close() + if err != nil { + t.Fatalf("Close: %v", err) + } + + buf := zipBytes.Bytes() + p := findSignatureInBlock(buf) + if p < 0 { + t.Fatalf("Missing signature") + } + + b := readBuf(buf[p+4:]) // skip signature + d := &directoryEnd{ + diskNbr: uint32(b.uint16()), + dirDiskNbr: uint32(b.uint16()), + dirRecordsThisDisk: uint64(b.uint16()), + directoryRecords: uint64(b.uint16()), + directorySize: uint64(b.uint32()), + directoryOffset: uint64(b.uint32()), + commentLen: b.uint16(), + } + + // p7zip 16.02 wants regular end record directoryRecords to be accurate. + if g, w := d.directoryRecords, uint64(1); g != w { + t.Errorf("wanted directoryRecords %d, got %d", w, g) + } + + if g, w := d.directorySize, uint64(uint32max); g != w { + t.Errorf("wanted directorySize %d, got %d", w, g) + } + + if g, w := d.directoryOffset, uint64(uint32max); g != w { + t.Errorf("wanted directoryOffset %d, got %d", w, g) + } + + r := bytes.NewReader(buf) + + p64, err := findDirectory64End(r, int64(p)) + if err != nil { + t.Fatalf("findDirectory64End: %v", err) + } + if p < 0 { + t.Fatalf("findDirectory64End: not found") + } + err = readDirectory64End(r, p64, d) + if err != nil { + t.Fatalf("readDirectory64End: %v", err) + } + + if g, w := d.directoryRecords, uint64(1); g != w { + t.Errorf("wanted directoryRecords %d, got %d", w, g) + } + + if g, w := d.directoryOffset, uint64(uint32max); g <= w { + t.Errorf("wanted directoryOffset > %d, got %d", w, g) + } +} diff --git a/third_party/zip/writer.go b/third_party/zip/writer.go index 8dd986eb1..f5268385d 100644 --- a/third_party/zip/writer.go +++ b/third_party/zip/writer.go @@ -155,7 +155,14 @@ func (w *Writer) Close() error { // store max values in the regular end record to signal that // that the zip64 values should be used instead - records = uint16max + // BEGIN ANDROID CHANGE: only store uintmax for the number of entries in the regular + // end record if it doesn't fit. p7zip 16.02 rejects zip files where the number of + // entries in the regular end record is larger than the number of entries counted + // in the central directory. + if records > uint16max { + records = uint16max + } + // END ANDROID CHANGE size = uint32max offset = uint32max }