archive/zip: fix size value in ZIP64 end central directory record

Section 4.3.14.1 of the ZIP file format
spec (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) says,

    The value stored into the "size of zip64 end of central directory
    record" should be the size of the remaining record and should not
    include the leading 12 bytes.

We were previously writing the full size, including the 12 bytes.

Fixes #9857

Change-Id: I7cf1fc8457c5f306717cbcf61e02304ab549781f
Reviewed-on: https://go-review.googlesource.com/4760
Reviewed-by: Andrew Gerrand <adg@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go
index 170beec..6a71887 100644
--- a/src/archive/zip/writer.go
+++ b/src/archive/zip/writer.go
@@ -122,15 +122,15 @@
 
 		// zip64 end of central directory record
 		b.uint32(directory64EndSignature)
-		b.uint64(directory64EndLen)
-		b.uint16(zipVersion45) // version made by
-		b.uint16(zipVersion45) // version needed to extract
-		b.uint32(0)            // number of this disk
-		b.uint32(0)            // number of the disk with the start of the central directory
-		b.uint64(records)      // total number of entries in the central directory on this disk
-		b.uint64(records)      // total number of entries in the central directory
-		b.uint64(size)         // size of the central directory
-		b.uint64(offset)       // offset of start of central directory with respect to the starting disk number
+		b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
+		b.uint16(zipVersion45)           // version made by
+		b.uint16(zipVersion45)           // version needed to extract
+		b.uint32(0)                      // number of this disk
+		b.uint32(0)                      // number of the disk with the start of the central directory
+		b.uint64(records)                // total number of entries in the central directory on this disk
+		b.uint64(records)                // total number of entries in the central directory
+		b.uint64(size)                   // size of the central directory
+		b.uint64(offset)                 // offset of start of central directory with respect to the starting disk number
 
 		// zip64 end of central directory locator
 		b.uint32(directory64LocSignature)
diff --git a/src/archive/zip/zip_test.go b/src/archive/zip/zip_test.go
index 32a16a7..f00ff47 100644
--- a/src/archive/zip/zip_test.go
+++ b/src/archive/zip/zip_test.go
@@ -229,10 +229,11 @@
 		t.Skip("slow test; skipping")
 	}
 	const size = 1 << 32 // before the "END\n" part
-	testZip64(t, size)
+	buf := testZip64(t, size)
+	testZip64DirectoryRecordLength(buf, t)
 }
 
-func testZip64(t testing.TB, size int64) {
+func testZip64(t testing.TB, size int64) *rleBuffer {
 	const chunkSize = 1024
 	chunks := int(size / chunkSize)
 	// write 2^32 bytes plus "END\n" to a zip file
@@ -302,6 +303,37 @@
 	if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want {
 		t.Errorf("UncompressedSize64 %d, want %d", got, want)
 	}
+
+	return buf
+}
+
+// Issue 9857
+func testZip64DirectoryRecordLength(buf *rleBuffer, t *testing.T) {
+	d := make([]byte, 1024)
+	if _, err := buf.ReadAt(d, buf.Size()-int64(len(d))); err != nil {
+		t.Fatal("read:", err)
+	}
+
+	sigOff := findSignatureInBlock(d)
+	dirOff, err := findDirectory64End(buf, buf.Size()-int64(len(d))+int64(sigOff))
+	if err != nil {
+		t.Fatal("findDirectory64End:", err)
+	}
+
+	d = make([]byte, directory64EndLen)
+	if _, err := buf.ReadAt(d, dirOff); err != nil {
+		t.Fatal("read:", err)
+	}
+
+	b := readBuf(d)
+	if sig := b.uint32(); sig != directory64EndSignature {
+		t.Fatalf("Expected directory64EndSignature (%d), got %d", directory64EndSignature, sig)
+	}
+
+	size := b.uint64()
+	if size != directory64EndLen-12 {
+		t.Fatalf("Expected length of %d, got %d", directory64EndLen-12, size)
+	}
 }
 
 func testInvalidHeader(h *FileHeader, t *testing.T) {