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
This commit is contained in:
Colin Cross 2021-05-07 15:59:32 -07:00
parent ee7e359131
commit 06eea2c9b8
2 changed files with 88 additions and 1 deletions

View File

@ -140,3 +140,83 @@ func TestCopyFromZip64(t *testing.T) {
t.Errorf("Expected UnompressedSize64 %d, got %d", w, g) 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)
}
}

View File

@ -155,7 +155,14 @@ func (w *Writer) Close() error {
// store max values in the regular end record to signal that // store max values in the regular end record to signal that
// that the zip64 values should be used instead // that the zip64 values should be used instead
// 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 records = uint16max
}
// END ANDROID CHANGE
size = uint32max size = uint32max
offset = uint32max offset = uint32max
} }