8015666: test/tools/pack200/TimeStamp.java failing
Summary: to keep the default behavior of ZOS unchanged, if ze extra time not explicitly set
Reviewed-by: alanb, ksrini
--- a/jdk/src/share/classes/java/util/zip/ZipConstants.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipConstants.java Thu Aug 08 12:03:04 2013 -0700
@@ -71,10 +71,17 @@
/*
* Extra field header ID
*/
- static final int EXTID_ZIP64 = 0x0001; // Zip64
- static final int EXTID_NTFS = 0x000a; // NTFS
- static final int EXTID_UNIX = 0x000d; // UNIX
- static final int EXTID_EXTT = 0x5455; // Info-ZIP Extended Timestamp
+ static final int EXTID_ZIP64 = 0x0001; // Zip64
+ static final int EXTID_NTFS = 0x000a; // NTFS
+ static final int EXTID_UNIX = 0x000d; // UNIX
+ static final int EXTID_EXTT = 0x5455; // Info-ZIP Extended Timestamp
+
+ /*
+ * EXTT timestamp flags
+ */
+ static final int EXTT_FLAG_LMT = 0x1; // LastModifiedTime
+ static final int EXTT_FLAG_LAT = 0x2; // LastAccessTime
+ static final int EXTT_FLAT_CT = 0x4; // CreationTime
/*
* Central directory (CEN) header field offsets
--- a/jdk/src/share/classes/java/util/zip/ZipEntry.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipEntry.java Thu Aug 08 12:03:04 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,11 @@
package java.util.zip;
+import static java.util.zip.ZipUtils.*;
+import java.nio.file.attribute.FileTime;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
/**
* This class is used to represent a ZIP file entry.
*
@@ -32,8 +37,12 @@
*/
public
class ZipEntry implements ZipConstants, Cloneable {
+
String name; // entry name
- long mtime = -1; // last modification time
+ long time = -1; // last modification time
+ FileTime mtime; // last modification time, from extra field data
+ FileTime atime; // last access time, from extra field data
+ FileTime ctime; // creation time, from extra field data
long crc = -1; // crc-32 of entry data
long size = -1; // uncompressed size of entry data
long csize = -1; // compressed size of entry data
@@ -55,15 +64,15 @@
/**
* Creates a new zip entry with the specified name.
*
- * @param name the entry name
- * @exception NullPointerException if the entry name is null
- * @exception IllegalArgumentException if the entry name is longer than
- * 0xFFFF bytes
+ * @param name
+ * The entry name
+ *
+ * @throws NullPointerException if the entry name is null
+ * @throws IllegalArgumentException if the entry name is longer than
+ * 0xFFFF bytes
*/
public ZipEntry(String name) {
- if (name == null) {
- throw new NullPointerException();
- }
+ Objects.requireNonNull(name, "name");
if (name.length() > 0xFFFF) {
throw new IllegalArgumentException("entry name too long");
}
@@ -73,11 +82,19 @@
/**
* Creates a new zip entry with fields taken from the specified
* zip entry.
- * @param e a zip Entry object
+ *
+ * @param e
+ * A zip Entry object
+ *
+ * @throws NullPointerException if the entry object is null
*/
public ZipEntry(ZipEntry e) {
+ Objects.requireNonNull(e, "entry");
name = e.name;
+ time = e.time;
mtime = e.mtime;
+ atime = e.atime;
+ ctime = e.ctime;
crc = e.crc;
size = e.size;
csize = e.csize;
@@ -103,33 +120,178 @@
/**
* Sets the last modification time of the entry.
*
- * @param time the last modification time of the entry in milliseconds since the epoch
+ * <p> If the entry is output to a ZIP file or ZIP file formatted
+ * output stream the last modification time set by this method will
+ * be stored into the {@code date and time fields} of the zip file
+ * entry and encoded in standard {@code MS-DOS date and time format}.
+ * The {@link java.util.TimeZone#getDefault() default TimeZone} is
+ * used to convert the epoch time to the MS-DOS data and time.
+ *
+ * @param time
+ * The last modification time of the entry in milliseconds
+ * since the epoch
+ *
* @see #getTime()
+ * @see #getLastModifiedTime()
*/
public void setTime(long time) {
+ this.time = time;
+ this.mtime = null;
+ }
+
+ /**
+ * Returns the last modification time of the entry.
+ *
+ * <p> If the entry is read from a ZIP file or ZIP file formatted
+ * input stream, this is the last modification time from the {@code
+ * date and time fields} of the zip file entry. The
+ * {@link java.util.TimeZone#getDefault() default TimeZone} is used
+ * to convert the standard MS-DOS formatted date and time to the
+ * epoch time.
+ *
+ * @return The last modification time of the entry in milliseconds
+ * since the epoch, or -1 if not specified
+ *
+ * @see #setTime(long)
+ * @see #setLastModifiedTime(FileTime)
+ */
+ public long getTime() {
+ return time;
+ }
+
+ /**
+ * Sets the last modification time of the entry.
+ *
+ * <p> When output to a ZIP file or ZIP file formatted output stream
+ * the last modification time set by this method will be stored into
+ * zip file entry's {@code date and time fields} in {@code standard
+ * MS-DOS date and time format}), and the extended timestamp fields
+ * in {@code optional extra data} in UTC time.
+ *
+ * @param time
+ * The last modification time of the entry
+ * @return This zip entry
+ *
+ * @throws NullPointerException if the {@code time} is null
+ *
+ * @see #getLastModifiedTime()
+ * @since 1.8
+ */
+ public ZipEntry setLastModifiedTime(FileTime time) {
+ Objects.requireNonNull(name, "time");
this.mtime = time;
+ this.time = time.to(TimeUnit.MILLISECONDS);
+ return this;
}
/**
* Returns the last modification time of the entry.
- * <p> The last modificatin time may come from zip entry's extensible
- * data field {@code NTFS} or {@code Info-ZIP Extended Timestamp}, if
- * the entry is read from {@link ZipInputStream} or {@link ZipFile}.
+ *
+ * <p> If the entry is read from a ZIP file or ZIP file formatted
+ * input stream, this is the last modification time from the zip
+ * file entry's {@code optional extra data} if the extended timestamp
+ * fields are present. Otherwise the last modification time is read
+ * from the entry's {@code date and time fields}, the {@link
+ * java.util.TimeZone#getDefault() default TimeZone} is used to convert
+ * the standard MS-DOS formatted date and time to the epoch time.
+ *
+ * @return The last modification time of the entry, null if not specified
+ *
+ * @see #setLastModifiedTime(FileTime)
+ * @since 1.8
+ */
+ public FileTime getLastModifiedTime() {
+ if (mtime != null)
+ return mtime;
+ if (time == -1)
+ return null;
+ return FileTime.from(time, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Sets the last access time of the entry.
+ *
+ * <p> If set, the last access time will be stored into the extended
+ * timestamp fields of entry's {@code optional extra data}, when output
+ * to a ZIP file or ZIP file formatted stream.
+ *
+ * @param time
+ * The last access time of the entry
+ * @return This zip entry
+ *
+ * @throws NullPointerException if the {@code time} is null
+ *
+ * @see #getLastAccessTime()
+ * @since 1.8
+ */
+ public ZipEntry setLastAccessTime(FileTime time) {
+ Objects.requireNonNull(name, "time");
+ this.atime = time;
+ return this;
+ }
+
+ /**
+ * Returns the last access time of the entry.
*
- * @return the last modification time of the entry, or -1 if not specified
- * @see #setTime(long)
+ * <p> The last access time is from the extended timestamp fields
+ * of entry's {@code optional extra data} when read from a ZIP file
+ * or ZIP file formatted stream.
+ *
+ * @return The last access time of the entry, null if not specified
+
+ * @see #setLastAccessTime(long)
+ * @since 1.8
*/
- public long getTime() {
- return mtime;
+ public FileTime getLastAccessTime() {
+ return atime;
+ }
+
+ /**
+ * Sets the creation time of the entry.
+ *
+ * <p> If set, the creation time will be stored into the extended
+ * timestamp fields of entry's {@code optional extra data}, when
+ * output to a ZIP file or ZIP file formatted stream.
+ *
+ * @param time
+ * The creation time of the entry
+ * @return This zip entry
+ *
+ * @throws NullPointerException if the {@code time} is null
+ *
+ * @see #getCreationTime()
+ * @since 1.8
+ */
+ public ZipEntry setCreationTime(FileTime time) {
+ Objects.requireNonNull(name, "time");
+ this.ctime = time;
+ return this;
+ }
+
+ /**
+ * Returns the creation time of the entry.
+ *
+ * <p> The creation time is from the extended timestamp fields of
+ * entry's {@code optional extra data} when read from a ZIP file
+ * or ZIP file formatted stream.
+ *
+ * @return the creation time of the entry, null if not specified
+ * @see #setCreationTime(FileTime)
+ * @since 1.8
+ */
+ public FileTime getCreationTime() {
+ return ctime;
}
/**
* Sets the uncompressed size of the entry data.
+ *
* @param size the uncompressed size in bytes
- * @exception IllegalArgumentException if the specified size is less
- * than 0, is greater than 0xFFFFFFFF when
- * <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
- * or is less than 0 when ZIP64 is supported
+ *
+ * @throws IllegalArgumentException if the specified size is less
+ * than 0, is greater than 0xFFFFFFFF when
+ * <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+ * or is less than 0 when ZIP64 is supported
* @see #getSize()
*/
public void setSize(long size) {
@@ -140,7 +302,8 @@
}
/**
- * Returns the uncompressed size of the entry data, or -1 if not known.
+ * Returns the uncompressed size of the entry data.
+ *
* @return the uncompressed size of the entry data, or -1 if not known
* @see #setSize(long)
*/
@@ -149,9 +312,11 @@
}
/**
- * Returns the size of the compressed entry data, or -1 if not known.
- * In the case of a stored entry, the compressed size will be the same
+ * Returns the size of the compressed entry data.
+ *
+ * <p> In the case of a stored entry, the compressed size will be the same
* as the uncompressed size of the entry.
+ *
* @return the size of the compressed entry data, or -1 if not known
* @see #setCompressedSize(long)
*/
@@ -161,7 +326,9 @@
/**
* Sets the size of the compressed entry data.
+ *
* @param csize the compressed size to set to
+ *
* @see #getCompressedSize()
*/
public void setCompressedSize(long csize) {
@@ -170,9 +337,11 @@
/**
* Sets the CRC-32 checksum of the uncompressed entry data.
+ *
* @param crc the CRC-32 value
- * @exception IllegalArgumentException if the specified CRC-32 value is
- * less than 0 or greater than 0xFFFFFFFF
+ *
+ * @throws IllegalArgumentException if the specified CRC-32 value is
+ * less than 0 or greater than 0xFFFFFFFF
* @see #getCrc()
*/
public void setCrc(long crc) {
@@ -183,10 +352,11 @@
}
/**
- * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
- * not known.
+ * Returns the CRC-32 checksum of the uncompressed entry data.
+ *
* @return the CRC-32 checksum of the uncompressed entry data, or -1 if
* not known
+ *
* @see #setCrc(long)
*/
public long getCrc() {
@@ -195,9 +365,11 @@
/**
* Sets the compression method for the entry.
+ *
* @param method the compression method, either STORED or DEFLATED
- * @exception IllegalArgumentException if the specified compression
- * method is invalid
+ *
+ * @throws IllegalArgumentException if the specified compression
+ * method is invalid
* @see #getMethod()
*/
public void setMethod(int method) {
@@ -208,7 +380,8 @@
}
/**
- * Returns the compression method of the entry, or -1 if not specified.
+ * Returns the compression method of the entry.
+ *
* @return the compression method of the entry, or -1 if not specified
* @see #setMethod(int)
*/
@@ -218,21 +391,104 @@
/**
* Sets the optional extra field data for the entry.
- * @param extra the extra field data bytes
- * @exception IllegalArgumentException if the length of the specified
- * extra field data is greater than 0xFFFF bytes
+ *
+ * <p> Invoking this method may change this entry's last modification
+ * time, last access time and creation time, if the {@code extra} field
+ * data includes the extensible timestamp fields, such as {@code NTFS tag
+ * 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
+ * <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
+ * Application Note 970311</a>.
+ *
+ * @param extra
+ * The extra field data bytes
+ *
+ * @throws IllegalArgumentException if the length of the specified
+ * extra field data is greater than 0xFFFF bytes
+ *
* @see #getExtra()
*/
public void setExtra(byte[] extra) {
- if (extra != null && extra.length > 0xFFFF) {
- throw new IllegalArgumentException("invalid extra field length");
+ setExtra0(extra, false);
+ }
+
+ /**
+ * Sets the optional extra field data for the entry.
+ *
+ * @param extra
+ * the extra field data bytes
+ * @param doZIP64
+ * if true, set size and csize from ZIP64 fields if present
+ */
+ void setExtra0(byte[] extra, boolean doZIP64) {
+ if (extra != null) {
+ if (extra.length > 0xFFFF) {
+ throw new IllegalArgumentException("invalid extra field length");
+ }
+ // extra fields are in "HeaderID(2)DataSize(2)Data... format
+ int off = 0;
+ int len = extra.length;
+ while (off + 4 < len) {
+ int tag = get16(extra, off);
+ int sz = get16(extra, off + 2);
+ off += 4;
+ if (off + sz > len) // invalid data
+ break;
+ switch (tag) {
+ case EXTID_ZIP64:
+ if (doZIP64) {
+ // LOC extra zip64 entry MUST include BOTH original
+ // and compressed file size fields.
+ // If invalid zip64 extra fields, simply skip. Even
+ // it's rare, it's possible the entry size happens to
+ // be the magic value and it "accidently" has some
+ // bytes in extra match the id.
+ if (sz >= 16) {
+ size = get64(extra, off);
+ csize = get64(extra, off + 8);
+ }
+ }
+ break;
+ case EXTID_NTFS:
+ int pos = off + 4; // reserved 4 bytes
+ if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
+ break;
+ mtime = winTimeToFileTime(get64(extra, pos + 4));
+ atime = winTimeToFileTime(get64(extra, pos + 12));
+ ctime = winTimeToFileTime(get64(extra, pos + 20));
+ break;
+ case EXTID_EXTT:
+ int flag = Byte.toUnsignedInt(extra[off]);
+ int sz0 = 1;
+ // The CEN-header extra field contains the modification
+ // time only, or no timestamp at all. 'sz' is used to
+ // flag its presence or absence. But if mtime is present
+ // in LOC it must be present in CEN as well.
+ if ((flag & 0x1) != 0 && (sz0 + 4) <= sz) {
+ mtime = unixTimeToFileTime(get32(extra, off + sz0));
+ sz0 += 4;
+ }
+ if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
+ atime = unixTimeToFileTime(get32(extra, off + sz0));
+ sz0 += 4;
+ }
+ if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
+ ctime = unixTimeToFileTime(get32(extra, off + sz0));
+ sz0 += 4;
+ }
+ break;
+ default:
+ }
+ off += sz;
+ }
}
this.extra = extra;
}
/**
- * Returns the extra field data for the entry, or null if none.
+ * Returns the extra field data for the entry.
+ *
* @return the extra field data for the entry, or null if none
+ *
* @see #setExtra(byte[])
*/
public byte[] getExtra() {
@@ -255,8 +511,10 @@
}
/**
- * Returns the comment string for the entry, or null if none.
+ * Returns the comment string for the entry.
+ *
* @return the comment string for the entry, or null if none
+ *
* @see #setComment(String)
*/
public String getComment() {
--- a/jdk/src/share/classes/java/util/zip/ZipFile.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipFile.java Thu Aug 08 12:03:04 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -567,44 +567,12 @@
e.name = zc.toString(bname, bname.length);
}
}
+ e.time = dosToJavaTime(getEntryTime(jzentry));
e.crc = getEntryCrc(jzentry);
e.size = getEntrySize(jzentry);
- e. csize = getEntryCSize(jzentry);
+ e.csize = getEntryCSize(jzentry);
e.method = getEntryMethod(jzentry);
- e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA);
- if (e.extra != null) {
- byte[] extra = e.extra;
- int len = e.extra.length;
- int off = 0;
- while (off + 4 < len) {
- int pos = off;
- int tag = get16(extra, pos);
- int sz = get16(extra, pos + 2);
- pos += 4;
- if (pos + sz > len) // invalid data
- break;
- switch (tag) {
- case EXTID_NTFS:
- pos += 4; // reserved 4 bytes
- if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
- break;
- e.mtime = winToJavaTime(get64(extra, pos + 4));
- break;
- case EXTID_EXTT:
- int flag = Byte.toUnsignedInt(extra[pos++]);
- if ((flag & 0x1) != 0) {
- e.mtime = unixToJavaTime(get32(extra, pos));
- pos += 4;
- }
- break;
- default: // unknown tag
- }
- off += (sz + 4);
- }
- }
- if (e.mtime == -1) {
- e.mtime = dosToJavaTime(getEntryTime(jzentry));
- }
+ e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false);
byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
if (bcomm == null) {
e.comment = null;
--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java Thu Aug 08 12:03:04 2013 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -288,9 +288,9 @@
int len = get16(tmpbuf, LOCNAM);
int blen = b.length;
if (len > blen) {
- do
+ do {
blen = blen * 2;
- while (len > blen);
+ } while (len > blen);
b = new byte[blen];
}
readFully(b, 0, len);
@@ -303,7 +303,7 @@
throw new ZipException("encrypted ZIP entry not supported");
}
e.method = get16(tmpbuf, LOCHOW);
- e.mtime = dosToJavaTime(get32(tmpbuf, LOCTIM));
+ e.time = dosToJavaTime(get32(tmpbuf, LOCTIM));
if ((flag & 8) == 8) {
/* "Data Descriptor" present */
if (e.method != DEFLATED) {
@@ -319,49 +319,7 @@
if (len > 0) {
byte[] extra = new byte[len];
readFully(extra, 0, len);
- e.setExtra(extra);
- // extra fields are in "HeaderID(2)DataSize(2)Data... format
- int off = 0;
- while (off + 4 < len) {
- int pos = off;
- int tag = get16(extra, pos);
- int sz = get16(extra, pos + 2);
- pos += 4;
- if (pos + sz > len) // invalid data
- break;
- switch (tag) {
- case EXTID_ZIP64 :
- // LOC extra zip64 entry MUST include BOTH original and
- // compressed file size fields.
- //
- // If invalid zip64 extra fields, simply skip. Even it's
- // rare, it's possible the entry size happens to be
- // the magic value and it "accidently" has some bytes
- // in extra match the id.
- if (sz >= 16 && (pos + sz) <= len ) {
- e.size = get64(extra, pos);
- e.csize = get64(extra, pos + 8);
- }
- break;
- case EXTID_NTFS:
- pos += 4; // reserved 4 bytes
- if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
- break;
- // override the loc field, NTFS time has 'microsecond' granularity
- e.mtime = winToJavaTime(get64(extra, pos + 4));
- break;
- case EXTID_EXTT:
- int flag = Byte.toUnsignedInt(extra[pos++]);
- if ((flag & 0x1) != 0) {
- e.mtime = unixToJavaTime(get32(extra, pos));
- pos += 4;
- }
- break;
- default: // unknown tag
- }
- off += (sz + 4);
- }
-
+ e.setExtra0(extra, true);
}
return e;
}
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Thu Aug 08 12:03:04 2013 -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));
+ 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));
}
- if (e.extra != null) {
- writeBytes(e.extra, 0, e.extra.length);
- }
+ 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) {
+ elen += (elenZIP64 + 4);// + headid(2) + datasize(2)
}
- if (hasZip64) {
- // + headid(2) + datasize(2)
- elen += (elenZIP64 + 4);
+ // 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 (!foundEXTT)
- elen += 9; // Info-ZIP's Extended Timestamp
+ if (e.atime != null) {
+ flagEXTT |= EXTT_FLAG_LAT;
+ }
+ if (e.ctime != null) {
+ flagEXTT |= EXTT_FLAT_CT;
+ }
+ if (flagEXTT != 0) {
+ elen += 5; // headid + sz + flag
+ }
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.mtime != null) {
+ writeShort(5); // flag + mtime
+ writeByte(flagEXTT);
+ writeInt(fileTimeToUnixTime(e.mtime));
+ } else {
+ writeShort(1); // flag only
+ writeByte(flagEXTT);
+ }
}
- if (e.extra != null) {
- writeBytes(e.extra, 0, e.extra.length);
- }
+ 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 {
--- a/jdk/src/share/classes/java/util/zip/ZipUtils.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipUtils.java Thu Aug 08 12:03:04 2013 -0700
@@ -25,42 +25,45 @@
package java.util.zip;
+import java.nio.file.attribute.FileTime;
import java.util.Date;
import java.util.concurrent.TimeUnit;
+import static java.util.zip.ZipConstants.*;
+import static java.util.zip.ZipConstants64.*;
+
class ZipUtils {
// used to adjust values between Windows and java epoch
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
/**
- * Converts Windows time (in microseconds, UTC/GMT) time to Java time.
+ * Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
*/
- public static final long winToJavaTime(long wtime) {
- return TimeUnit.MILLISECONDS.convert(
- wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS);
+ public static final FileTime winTimeToFileTime(long wtime) {
+ return FileTime.from(wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS,
+ TimeUnit.MICROSECONDS);
}
/**
- * Converts Java time to Windows time.
+ * Converts FileTime to Windows time.
*/
- public static final long javaToWinTime(long time) {
- return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS)
- - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
+ public static final long fileTimeToWinTime(FileTime ftime) {
+ return (ftime.to(TimeUnit.MICROSECONDS) - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
}
/**
- * Converts "standard Unix time"(in seconds, UTC/GMT) to Java time
+ * Converts "standard Unix time"(in seconds, UTC/GMT) to FileTime
*/
- public static final long unixToJavaTime(long utime) {
- return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS);
+ public static final FileTime unixTimeToFileTime(long utime) {
+ return FileTime.from(utime, TimeUnit.SECONDS);
}
/**
- * Converts Java time to "standard Unix time".
+ * Converts FileTime to "standard Unix time".
*/
- public static final long javaToUnixTime(long time) {
- return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS);
+ public static final long fileTimeToUnixTime(FileTime ftime) {
+ return ftime.to(TimeUnit.SECONDS);
}
/**
@@ -92,7 +95,6 @@
d.getSeconds() >> 1;
}
-
/**
* Fetches unsigned 16-bit value from byte array at specified offset.
* The bytes are assumed to be in Intel (little-endian) byte order.
@@ -116,5 +118,4 @@
public static final long get64(byte b[], int off) {
return get32(b, off) | (get32(b, off+4) << 32);
}
-
}
--- a/jdk/test/ProblemList.txt Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/test/ProblemList.txt Thu Aug 08 12:03:04 2013 -0700
@@ -335,8 +335,6 @@
# Tests take too long, on sparcs see 7143279
tools/pack200/CommandLineTests.java solaris-all, macosx-all
tools/pack200/Pack200Test.java solaris-all, macosx-all
-# 8015666
-tools/pack200/TimeStamp.java generic-all
# 8007410
tools/launcher/FXLauncherTest.java linux-all
--- a/jdk/test/java/util/zip/TestExtraTime.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/test/java/util/zip/TestExtraTime.java Thu Aug 08 12:03:04 2013 -0700
@@ -23,14 +23,19 @@
/**
* @test
- * @bug 4759491 6303183 7012868
+ * @bug 4759491 6303183 7012868 8015666
* @summary Test ZOS and ZIS timestamp in extra field correctly
*/
import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.FileTime;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -41,14 +46,32 @@
File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java");
if (src.exists()) {
- long mtime = src.lastModified();
- test(mtime, null);
- test(10, null); // ms-dos 1980 epoch problem
- test(mtime, TimeZone.getTimeZone("Asia/Shanghai"));
+ long time = src.lastModified();
+ FileTime mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
+ FileTime atime = FileTime.from(time + 300000, TimeUnit.MILLISECONDS);
+ FileTime ctime = FileTime.from(time - 300000, TimeUnit.MILLISECONDS);
+ TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
+
+ test(mtime, null, null, null);
+ // ms-dos 1980 epoch problem
+ test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null);
+ // non-default tz
+ test(mtime, null, null, tz);
+
+ test(mtime, atime, null, null);
+ test(mtime, null, ctime, null);
+ test(mtime, atime, ctime, null);
+
+ test(mtime, atime, null, tz);
+ test(mtime, null, ctime, tz);
+ test(mtime, atime, ctime, tz);
}
}
- private static void test(long mtime, TimeZone tz) throws Throwable {
+ static void test(FileTime mtime, FileTime atime, FileTime ctime,
+ TimeZone tz) throws Throwable {
+ System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
+ mtime, atime, ctime);
TimeZone tz0 = TimeZone.getDefault();
if (tz != null) {
TimeZone.setDefault(tz);
@@ -57,23 +80,55 @@
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("TestExtreTime.java");
- ze.setTime(mtime);
+ ze.setLastModifiedTime(mtime);
+ if (atime != null)
+ ze.setLastAccessTime(atime);
+ if (ctime != null)
+ ze.setCreationTime(ctime);
zos.putNextEntry(ze);
zos.write(new byte[] { 1,2 ,3, 4});
zos.close();
if (tz != null) {
TimeZone.setDefault(tz0);
}
+ // ZipInputStream
ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(baos.toByteArray()));
ze = zis.getNextEntry();
zis.close();
+ check(mtime, atime, ctime, ze);
- System.out.printf("%tc => %tc%n", mtime, ze.getTime());
+ // ZipFile
+ Path zpath = Paths.get(System.getProperty("test.dir", "."),
+ "TestExtraTimp.zip");
+ Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
+ ZipFile zf = new ZipFile(zpath.toFile());
+ ze = zf.getEntry("TestExtreTime.java");
+ // ZipFile read entry from cen, which does not have a/ctime,
+ // for now.
+ check(mtime, null, null, ze);
+ zf.close();
+ Files.delete(zpath);
+ }
- if (TimeUnit.MILLISECONDS.toSeconds(mtime) !=
- TimeUnit.MILLISECONDS.toSeconds(ze.getTime()))
- throw new RuntimeException("Timestamp storing failed!");
-
+ static void check(FileTime mtime, FileTime atime, FileTime ctime,
+ ZipEntry ze) {
+ /*
+ System.out.printf(" mtime [%tc]: [%tc]/[%tc]%n",
+ mtime.to(TimeUnit.MILLISECONDS),
+ ze.getTime(),
+ ze.getLastModifiedTime().to(TimeUnit.MILLISECONDS));
+ */
+ if (mtime.to(TimeUnit.SECONDS) !=
+ ze.getLastModifiedTime().to(TimeUnit.SECONDS))
+ throw new RuntimeException("Timestamp: storing mtime failed!");
+ if (atime != null &&
+ atime.to(TimeUnit.SECONDS) !=
+ ze.getLastAccessTime().to(TimeUnit.SECONDS))
+ throw new RuntimeException("Timestamp: storing atime failed!");
+ if (ctime != null &&
+ ctime.to(TimeUnit.SECONDS) !=
+ ze.getCreationTime().to(TimeUnit.SECONDS))
+ throw new RuntimeException("Timestamp: storing ctime failed!");
}
}
--- a/jdk/test/tools/pack200/TimeStamp.java Thu Aug 08 21:13:01 2013 +0800
+++ b/jdk/test/tools/pack200/TimeStamp.java Thu Aug 08 12:03:04 2013 -0700
@@ -88,6 +88,7 @@
unpackNative(packFile, pstFile);
verifyJar(goldenFile, pstFile);
pstFile.delete();
+ Utils.cleanup();
}
static void unpackNative(File packFile, File outFile) {
@@ -149,7 +150,6 @@
Utils.close(jf1);
Utils.close(jf2);
}
- Utils.cleanup();
if (errors > 0) {
throw new RuntimeException("FAIL:" + errors + " error(s) encounted");
}