Skip to content

Commit 7b451dc

Browse files
gwikdsnet
authored andcommitted
archive/zip: avoid data descriptor when writing directories
Java fails to unzip archives created by archive/zip because directories are written with the "data descriptor" flag (bit 3) set, but emits no such descriptor. To fix this, we explicitly clear the flag. Fixes golang#25215 Change-Id: Id3af4c7f863758197063df879717c1710f86c0e5 Reviewed-on: https://go-review.googlesource.com/110795 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com> Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
1 parent 8cd0094 commit 7b451dc

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

src/archive/zip/writer.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,6 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
263263
return nil, errors.New("archive/zip: invalid duplicate FileHeader")
264264
}
265265

266-
fh.Flags |= 0x8 // we will write a data descriptor
267-
268266
// The ZIP format has a sad state of affairs regarding character encoding.
269267
// Officially, the name and comment fields are supposed to be encoded
270268
// in CP-437 (which is mostly compatible with ASCII), unless the UTF-8
@@ -331,8 +329,17 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
331329
}
332330

333331
if strings.HasSuffix(fh.Name, "/") {
332+
// Set the compression method to Store to ensure data length is truly zero,
333+
// which the writeHeader method always encodes for the size fields.
334+
// This is necessary as most compression formats have non-zero lengths
335+
// even when compressing an empty string.
336+
fh.Method = Store
337+
fh.Flags &^= 0x8 // we will not write a data descriptor
338+
334339
ow = dirWriter{}
335340
} else {
341+
fh.Flags |= 0x8 // we will write a data descriptor
342+
336343
fw = &fileWriter{
337344
zipw: w.cw,
338345
compCount: &countWriter{w: w.cw},

src/archive/zip/writer_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package zip
66

77
import (
88
"bytes"
9+
"encoding/binary"
910
"fmt"
1011
"io"
1112
"io/ioutil"
@@ -310,6 +311,41 @@ func TestWriterDir(t *testing.T) {
310311
}
311312
}
312313

314+
func TestWriterDirAttributes(t *testing.T) {
315+
var buf bytes.Buffer
316+
w := NewWriter(&buf)
317+
if _, err := w.Create("dir/"); err != nil {
318+
t.Fatal(err)
319+
}
320+
if err := w.Close(); err != nil {
321+
t.Fatal(err)
322+
}
323+
324+
b := buf.Bytes()
325+
326+
var sig [4]byte
327+
binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
328+
329+
idx := bytes.Index(b, sig[:])
330+
if idx == -1 {
331+
t.Fatal("file header not found")
332+
}
333+
b = b[idx:]
334+
335+
if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0
336+
t.Errorf("unexpected method and flags: %v", b[6:10])
337+
}
338+
339+
if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero.
340+
t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
341+
}
342+
343+
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
344+
if bytes.Index(b, sig[:]) != -1 {
345+
t.Error("there should be no data descriptor")
346+
}
347+
}
348+
313349
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
314350
header := &FileHeader{
315351
Name: wt.Name,

0 commit comments

Comments
 (0)