Commit 7b451dc7 authored by Antonin Amand's avatar Antonin Amand Committed by Joe Tsai

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 #25215

Change-Id: Id3af4c7f863758197063df879717c1710f86c0e5
Reviewed-on: https://go-review.googlesource.com/110795Reviewed-by: default avatarJoe Tsai <thebrokentoaster@gmail.com>
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 8cd0094b
...@@ -263,8 +263,6 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { ...@@ -263,8 +263,6 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
return nil, errors.New("archive/zip: invalid duplicate FileHeader") return nil, errors.New("archive/zip: invalid duplicate FileHeader")
} }
fh.Flags |= 0x8 // we will write a data descriptor
// The ZIP format has a sad state of affairs regarding character encoding. // The ZIP format has a sad state of affairs regarding character encoding.
// Officially, the name and comment fields are supposed to be encoded // Officially, the name and comment fields are supposed to be encoded
// in CP-437 (which is mostly compatible with ASCII), unless the UTF-8 // 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) { ...@@ -331,8 +329,17 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
} }
if strings.HasSuffix(fh.Name, "/") { if strings.HasSuffix(fh.Name, "/") {
// Set the compression method to Store to ensure data length is truly zero,
// which the writeHeader method always encodes for the size fields.
// This is necessary as most compression formats have non-zero lengths
// even when compressing an empty string.
fh.Method = Store
fh.Flags &^= 0x8 // we will not write a data descriptor
ow = dirWriter{} ow = dirWriter{}
} else { } else {
fh.Flags |= 0x8 // we will write a data descriptor
fw = &fileWriter{ fw = &fileWriter{
zipw: w.cw, zipw: w.cw,
compCount: &countWriter{w: w.cw}, compCount: &countWriter{w: w.cw},
......
...@@ -6,6 +6,7 @@ package zip ...@@ -6,6 +6,7 @@ package zip
import ( import (
"bytes" "bytes"
"encoding/binary"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
...@@ -310,6 +311,41 @@ func TestWriterDir(t *testing.T) { ...@@ -310,6 +311,41 @@ func TestWriterDir(t *testing.T) {
} }
} }
func TestWriterDirAttributes(t *testing.T) {
var buf bytes.Buffer
w := NewWriter(&buf)
if _, err := w.Create("dir/"); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
b := buf.Bytes()
var sig [4]byte
binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
idx := bytes.Index(b, sig[:])
if idx == -1 {
t.Fatal("file header not found")
}
b = b[idx:]
if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0
t.Errorf("unexpected method and flags: %v", b[6:10])
}
if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero.
t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
}
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
if bytes.Index(b, sig[:]) != -1 {
t.Error("there should be no data descriptor")
}
}
func testCreate(t *testing.T, w *Writer, wt *WriteTest) { func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{ header := &FileHeader{
Name: wt.Name, Name: wt.Name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment