--- old/src/share/classes/java/util/zip/ZipOutputStream.java 2013-08-02 13:14:10.000000000 -0700 +++ new/src/share/classes/java/util/zip/ZipOutputStream.java 2013-08-02 13:14:09.000000000 -0700 @@ -59,8 +59,9 @@ "jdk.util.zip.inhibitZip64", "false"))); private static class XEntry { - public final ZipEntry entry; - public final long offset; + final ZipEntry entry; + final long offset; + long dostime; // last modification time in msdos format public XEntry(ZipEntry entry, long offset) { this.entry = entry; this.offset = offset; @@ -191,7 +192,9 @@ if (current != null) { closeEntry(); // close previous entry } - if (e.mtime == -1) { + if (e.time == -1) { + // by default, do NOT use extended timestamps in extra + // data, for now. e.setTime(System.currentTimeMillis()); } if (e.method == -1) { @@ -384,25 +387,20 @@ ZipEntry e = xentry.entry; int flag = e.flag; boolean hasZip64 = false; - int elen = (e.extra != null) ? e.extra.length : 0; - int eoff = 0; - boolean foundEXTT = false; // if EXTT already present - // do nothing. - while (eoff + 4 < elen) { - int tag = get16(e.extra, eoff); - int sz = get16(e.extra, eoff + 2); - if (tag == EXTID_EXTT) { - foundEXTT = true; - } - eoff += (4 + sz); - } + int elen = getExtraLen(e.extra); + + // keep a copy of dostime for writeCEN(), otherwise the tz + // sensitive local time entries in loc and cen might be + // different if the default tz get changed during writeLOC() + // and writeCEN() + xentry.dostime = javaToDosTime(e.time); + writeInt(LOCSIG); // LOC header signature if ((flag & 8) == 8) { writeShort(version(e)); // version needed to extract writeShort(flag); // general purpose bit flag writeShort(e.method); // compression method - writeInt(javaToDosTime(e.mtime)); // last modification time - + writeInt(xentry.dostime); // last modification time // store size, uncompressed size, and crc-32 in data descriptor // immediately following compressed entry data writeInt(0); @@ -417,7 +415,7 @@ } writeShort(flag); // general purpose bit flag writeShort(e.method); // compression method - writeInt(javaToDosTime(e.mtime)); // last modification time + writeInt(xentry.dostime); // last modification time writeInt(e.crc); // crc-32 if (hasZip64) { writeInt(ZIP64_MAGICVAL); @@ -430,8 +428,23 @@ } byte[] nameBytes = zc.getBytes(e.name); writeShort(nameBytes.length); - if (!foundEXTT) - elen += 9; // use Info-ZIP's ext time in extra + + int elenEXTT = 0; // info-zip extended timestamp + int flagEXTT = 0; + if (e.mtime != null) { + elenEXTT += 4; + flagEXTT |= EXTT_FLAG_LMT; + } + if (e.atime != null) { + elenEXTT += 4; + flagEXTT |= EXTT_FLAG_LAT; + } + if (e.ctime != null) { + elenEXTT += 4; + flagEXTT |= EXTT_FLAT_CT; + } + if (flagEXTT != 0) + elen += (elenEXTT + 5); // headid(2) + size(2) + flag(1) + data writeShort(elen); writeBytes(nameBytes, 0, nameBytes.length); if (hasZip64) { @@ -440,15 +453,18 @@ writeLong(e.size); writeLong(e.csize); } - if (!foundEXTT) { + if (flagEXTT != 0) { writeShort(EXTID_EXTT); - writeShort(5); // size for the folowing data block - writeByte(0x1); // flags byte, mtime only - writeInt(javaToUnixTime(e.mtime)); - } - if (e.extra != null) { - writeBytes(e.extra, 0, e.extra.length); + writeShort(elenEXTT + 1); // flag + data + writeByte(flagEXTT); + if (e.mtime != null) + writeInt(fileTimeToUnixTime(e.mtime)); + if (e.atime != null) + writeInt(fileTimeToUnixTime(e.atime)); + if (e.ctime != null) + writeInt(fileTimeToUnixTime(e.ctime)); } + writeExtra(e.extra); locoff = written; } @@ -506,31 +522,35 @@ } writeShort(flag); // general purpose bit flag writeShort(e.method); // compression method - writeInt(javaToDosTime(e.mtime)); // last modification time + // use the copy in xentry, which has been converted + // from e.time in writeLOC() + writeInt(xentry.dostime); // last modification time writeInt(e.crc); // crc-32 writeInt(csize); // compressed size writeInt(size); // uncompressed size byte[] nameBytes = zc.getBytes(e.name); writeShort(nameBytes.length); - int elen = (e.extra != null) ? e.extra.length : 0; - int eoff = 0; - boolean foundEXTT = false; // if EXTT already present - // do nothing. - while (eoff + 4 < elen) { - int tag = get16(e.extra, eoff); - int sz = get16(e.extra, eoff + 2); - if (tag == EXTID_EXTT) { - foundEXTT = true; - } - eoff += (4 + sz); - } + int elen = getExtraLen(e.extra); if (hasZip64) { - // + headid(2) + datasize(2) - elen += (elenZIP64 + 4); + elen += (elenZIP64 + 4);// + headid(2) + datasize(2) + } + // cen info-zip extended timestamp only outputs mtime + // but set the flag for a/ctime, if present in loc + int flagEXTT = 0; + if (e.mtime != null) { + elen += 4; // + mtime(4) + flagEXTT |= EXTT_FLAG_LMT; + } + if (e.atime != null) { + flagEXTT |= EXTT_FLAG_LAT; + } + if (e.ctime != null) { + flagEXTT |= EXTT_FLAT_CT; + } + if (flagEXTT != 0) { + elen += 5; // headid + sz + flag } - if (!foundEXTT) - elen += 9; // Info-ZIP's Extended Timestamp writeShort(elen); byte[] commentBytes; if (e.comment != null) { @@ -545,6 +565,8 @@ writeInt(0); // external file attributes (unused) writeInt(offset); // relative offset of local header writeBytes(nameBytes, 0, nameBytes.length); + + // take care of EXTID_ZIP64 and EXTID_EXTT if (hasZip64) { writeShort(ZIP64_EXTID);// Zip64 extra writeShort(elenZIP64); @@ -555,15 +577,18 @@ if (offset == ZIP64_MAGICVAL) writeLong(xentry.offset); } - if (!foundEXTT) { + if (flagEXTT != 0) { writeShort(EXTID_EXTT); - writeShort(5); - writeByte(0x1); // flags byte - writeInt(javaToUnixTime(e.mtime)); - } - if (e.extra != null) { - writeBytes(e.extra, 0, e.extra.length); + if (e.mtime != null) { + writeShort(5); // flag + mtime + writeByte(flagEXTT); + writeInt(fileTimeToUnixTime(e.mtime)); + } else { + writeShort(1); // flag only + writeByte(flagEXTT); + } } + writeExtra(e.extra); if (commentBytes != null) { writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff)); } @@ -627,6 +652,47 @@ } /* + * Returns the length of extra data without EXTT and ZIP64. + */ + private int getExtraLen(byte[] extra) { + if (extra == null) + return 0; + int skipped = 0; + int len = extra.length; + int off = 0; + while (off + 4 <= len) { + int tag = get16(extra, off); + int sz = get16(extra, off + 2); + if (tag == EXTID_EXTT || tag == EXTID_ZIP64) { + skipped += (sz + 4); + } + off += (sz + 4); + } + return len - skipped; + } + + /* + * Writes extra data without EXTT and ZIP64. + * + * Extra timestamp and ZIP64 data is handled/output separately + * in writeLOC and writeCEN. + */ + private void writeExtra(byte[] extra) throws IOException { + if (extra != null) { + int len = extra.length; + int off = 0; + while (off + 4 <= len) { + int tag = get16(extra, off); + int sz = get16(extra, off + 2); + if (tag != EXTID_EXTT && tag != EXTID_ZIP64) { + writeBytes(extra, off, sz + 4); + } + off += (sz + 4); + } + } + } + + /* * Writes a 8-bit byte to the output stream. */ private void writeByte(int v) throws IOException {