archive/zip: only use Extended Timestamp on non-zero MS-DOS timestamps
We should preserve the fact that a roundtrip read on fields with the zero
value should remain the zero for those that are reasonable to stay that way.
If the zero value for a MS-DOS timestamp was used, then it is sensible for
that zero value to also be read back later.
Fixes #17403
Change-Id: I32c3915eab180e91ddd2499007374f7b85f0bd76
Reviewed-on: https://go-review.googlesource.com/30811
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/archive/zip/struct.go b/src/archive/zip/struct.go
index 8e6eb84..287571e 100644
--- a/src/archive/zip/struct.go
+++ b/src/archive/zip/struct.go
@@ -65,7 +65,7 @@
zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field
ntfsExtraId = 0x000a // NTFS Extra Field
unixExtraId = 0x000d // UNIX Extra Field
- exttsExtraId = 0x5455 // Extra Timestamp Extra Field
+ exttsExtraId = 0x5455 // Extended Timestamp Extra Field
)
// FileHeader describes a file within a zip file.
diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go
index 2a747b8..4ab993d 100644
--- a/src/archive/zip/writer.go
+++ b/src/archive/zip/writer.go
@@ -99,14 +99,17 @@
b.uint32(h.UncompressedSize)
}
- mt := uint32(h.FileHeader.ModTime().Unix())
- var mbuf [9]byte // 2x uint16 + uint8 + uint32
- eb := writeBuf(mbuf[:])
- eb.uint16(exttsExtraId)
- eb.uint16(5) // size = uint8 + uint32
- eb.uint8(1) // flags = modtime
- eb.uint32(mt) // ModTime
- h.Extra = append(h.Extra, mbuf[:]...)
+ // use Extended Timestamp Extra Field.
+ if h.ModifiedTime != 0 || h.ModifiedDate != 0 {
+ mt := uint32(h.ModTime().Unix())
+ var mbuf [9]byte // 2x uint16 + uint8 + uint32
+ eb := writeBuf(mbuf[:])
+ eb.uint16(exttsExtraId)
+ eb.uint16(5) // size = uint8 + uint32
+ eb.uint8(1) // flags = modtime
+ eb.uint32(mt) // ModTime
+ h.Extra = append(h.Extra, mbuf[:]...)
+ }
b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra)))
diff --git a/src/archive/zip/zip_test.go b/src/archive/zip/zip_test.go
index 3a3c915..f166b76 100644
--- a/src/archive/zip/zip_test.go
+++ b/src/archive/zip/zip_test.go
@@ -13,6 +13,7 @@
"internal/testenv"
"io"
"io/ioutil"
+ "reflect"
"sort"
"strings"
"testing"
@@ -111,6 +112,44 @@
testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
}
+func TestZeroFileRoundTrip(t *testing.T) {
+ var b bytes.Buffer
+ w := NewWriter(&b)
+ if _, err := w.Create(""); err != nil {
+ t.Fatal(err)
+ }
+ if err := w.Close(); err != nil {
+ t.Fatal(err)
+ }
+ r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify that fields that should reasonably be the zero value stays
+ // as the zero value.
+ var want FileHeader
+ if len(r.File) != 1 {
+ t.Fatalf("len(r.File) = %d, want 1", len(r.File))
+ }
+ fh := r.File[0].FileHeader
+ got := FileHeader{
+ Name: fh.Name,
+ ModifiedTime: fh.ModifiedTime,
+ ModifiedDate: fh.ModifiedDate,
+ UncompressedSize: fh.UncompressedSize,
+ UncompressedSize64: fh.UncompressedSize64,
+ ExternalAttrs: fh.ExternalAttrs,
+ Comment: fh.Comment,
+ }
+ if len(fh.Extra) > 0 {
+ got.Extra = fh.Extra
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("FileHeader mismatch:\ngot %#v\nwant %#v", got, want)
+ }
+}
+
type repeatedByte struct {
off int64
b byte