aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2020-12-17 15:08:01 -0800
committerColin Cross <ccross@android.com>2020-12-17 15:08:50 -0800
commit373147baa9bf38a54140b75db4f4f39b17b02800 (patch)
tree371febfeb1c4256763e63b1dbe7dbd3dc36aac8c
parentf1c48afc31fcb07d19c9c3a088a1ccb3d67127ef (diff)
downloadsoong-373147baa9bf38a54140b75db4f4f39b17b02800.tar.gz
Support writing a ZIP64 file header
If the length of a stored file is more than 2^32 and a data descriptor is not being used then a ZIP64 extra is required in the file header to store the uncompressed and compressed lengths. Bug: 175055267 Test: TestCopyFromZip64 Change-Id: Id414b4c04f48aefabfd835bd8339333d36576375
-rw-r--r--third_party/zip/android_test.go66
-rw-r--r--third_party/zip/writer.go18
2 files changed, 81 insertions, 3 deletions
diff --git a/third_party/zip/android_test.go b/third_party/zip/android_test.go
index 5154a17b3..9932c1b13 100644
--- a/third_party/zip/android_test.go
+++ b/third_party/zip/android_test.go
@@ -74,3 +74,69 @@ func TestStripZip64Extras(t *testing.T) {
}
}
}
+
+func TestCopyFromZip64(t *testing.T) {
+ if testing.Short() {
+ t.Skip("slow test; skipping")
+ }
+
+ const size = uint32max + 1
+ fromZipBytes := &bytes.Buffer{}
+ fromZip := NewWriter(fromZipBytes)
+ w, err := fromZip.CreateHeaderAndroid(&FileHeader{
+ Name: "large",
+ Method: Store,
+ UncompressedSize64: size,
+ CompressedSize64: size,
+ })
+ if err != nil {
+ t.Fatalf("Create: %v", err)
+ }
+ _, err = w.Write(make([]byte, size))
+ if err != nil {
+ t.Fatalf("Write: %v", err)
+ }
+ err = fromZip.Close()
+ if err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+ fromZip = nil
+
+ fromZipReader, err := NewReader(bytes.NewReader(fromZipBytes.Bytes()), int64(fromZipBytes.Len()))
+ if err != nil {
+ t.Fatalf("NewReader: %v", err)
+ }
+
+ toZipBytes := &bytes.Buffer{}
+ toZip := NewWriter(toZipBytes)
+ err = toZip.CopyFrom(fromZipReader.File[0], fromZipReader.File[0].Name)
+ if err != nil {
+ t.Fatalf("CopyFrom: %v", err)
+ }
+
+ err = toZip.Close()
+ if err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+
+ // Save some memory
+ fromZipReader = nil
+ fromZipBytes.Reset()
+
+ toZipReader, err := NewReader(bytes.NewReader(toZipBytes.Bytes()), int64(toZipBytes.Len()))
+ if err != nil {
+ t.Fatalf("NewReader: %v", err)
+ }
+
+ if len(toZipReader.File) != 1 {
+ t.Fatalf("Expected 1 file in toZip, got %d", len(toZipReader.File))
+ }
+
+ if g, w := toZipReader.File[0].CompressedSize64, uint64(size); g != w {
+ t.Errorf("Expected CompressedSize64 %d, got %d", w, g)
+ }
+
+ if g, w := toZipReader.File[0].UncompressedSize64, uint64(size); g != w {
+ t.Errorf("Expected UnompressedSize64 %d, got %d", w, g)
+ }
+}
diff --git a/third_party/zip/writer.go b/third_party/zip/writer.go
index 4c5eb78b3..8dd986eb1 100644
--- a/third_party/zip/writer.go
+++ b/third_party/zip/writer.go
@@ -276,9 +276,6 @@ func writeHeader(w io.Writer, h *FileHeader) error {
} else {
b.uint32(h.CRC32)
- if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
- panic("skipping writing the data descriptor for a 64-bit value is not yet supported")
- }
compressedSize := uint32(h.CompressedSize64)
if compressedSize == 0 {
compressedSize = h.CompressedSize
@@ -289,6 +286,21 @@ func writeHeader(w io.Writer, h *FileHeader) error {
uncompressedSize = h.UncompressedSize
}
+ if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
+ // Sizes don't fit in a 32-bit field, put them in a zip64 extra instead.
+ compressedSize = uint32max
+ uncompressedSize = uint32max
+
+ // append a zip64 extra block to Extra
+ var buf [20]byte // 2x uint16 + 2x uint64
+ eb := writeBuf(buf[:])
+ eb.uint16(zip64ExtraId)
+ eb.uint16(16) // size = 2x uint64
+ eb.uint64(h.UncompressedSize64)
+ eb.uint64(h.CompressedSize64)
+ h.Extra = append(h.Extra, buf[:]...)
+ }
+
b.uint32(compressedSize)
b.uint32(uncompressedSize)
}