--- a/jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java Sat Feb 28 10:47:07 2015 +0800
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java Sat Feb 28 13:17:13 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2015, 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
@@ -41,7 +41,9 @@
class ZipEntry implements ZipConstants, Cloneable {
String name; // entry name
- long time = -1; // last modification time
+ long xdostime = -1; // last modification time (in extended DOS time,
+ // where milliseconds lost in conversion might
+ // be encoded into the upper half)
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
@@ -64,6 +66,28 @@
public static final int DEFLATED = 8;
/**
+ * DOS time constant for representing timestamps before 1980.
+ */
+ static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16);
+
+ /**
+ * Approximately 128 years, in milliseconds (ignoring leap years etc).
+ *
+ * This establish an approximate high-bound value for DOS times in
+ * milliseconds since epoch, used to enable an efficient but
+ * sufficient bounds check to avoid generating extended last modified
+ * time entries.
+ *
+ * Calculating the exact number is locale dependent, would require loading
+ * TimeZone data eagerly, and would make little practical sense. Since DOS
+ * times theoretically go to 2107 - with compatibility not guaranteed
+ * after 2099 - setting this to a time that is before but near 2099
+ * should be sufficient.
+ */
+ private static final long UPPER_DOSTIME_BOUND =
+ 128L * 365 * 24 * 60 * 60 * 1000;
+
+ /**
* Creates a new zip entry with the specified name.
*
* @param name
@@ -93,7 +117,7 @@
public ZipEntry(ZipEntry e) {
Objects.requireNonNull(e, "entry");
name = e.name;
- time = e.time;
+ xdostime = e.xdostime;
mtime = e.mtime;
atime = e.atime;
ctime = e.ctime;
@@ -137,8 +161,14 @@
* @see #getLastModifiedTime()
*/
public void setTime(long time) {
- this.time = time;
- this.mtime = null;
+ this.xdostime = javaToExtendedDosTime(time);
+ // Avoid setting the mtime field if time is in the valid
+ // range for a DOS time
+ if (xdostime != DOSTIME_BEFORE_1980 && time <= UPPER_DOSTIME_BOUND) {
+ this.mtime = null;
+ } else {
+ this.mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
+ }
}
/**
@@ -158,7 +188,10 @@
* @see #setLastModifiedTime(FileTime)
*/
public long getTime() {
- return time;
+ if (mtime != null) {
+ return mtime.toMillis();
+ }
+ return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1;
}
/**
@@ -181,7 +214,7 @@
*/
public ZipEntry setLastModifiedTime(FileTime time) {
this.mtime = Objects.requireNonNull(time, "lastModifiedTime");
- this.time = time.to(TimeUnit.MILLISECONDS);
+ this.xdostime = javaToExtendedDosTime(time.to(TimeUnit.MILLISECONDS));
return this;
}
@@ -204,9 +237,9 @@
public FileTime getLastModifiedTime() {
if (mtime != null)
return mtime;
- if (time == -1)
+ if (xdostime == -1)
return null;
- return FileTime.from(time, TimeUnit.MILLISECONDS);
+ return FileTime.from(getTime(), TimeUnit.MILLISECONDS);
}
/**
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Sat Feb 28 10:47:07 2015 +0800
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Sat Feb 28 13:17:13 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2015, 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
@@ -46,7 +46,6 @@
import java.util.stream.StreamSupport;
import static java.util.zip.ZipConstants64.*;
-import static java.util.zip.ZipUtils.*;
/**
* This class is used to read entries from a zip file.
@@ -567,7 +566,7 @@
e.name = zc.toString(bname, bname.length);
}
}
- e.time = dosToJavaTime(getEntryTime(jzentry));
+ e.xdostime = getEntryTime(jzentry);
e.crc = getEntryCrc(jzentry);
e.size = getEntrySize(jzentry);
e.csize = getEntryCSize(jzentry);
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipInputStream.java Sat Feb 28 10:47:07 2015 +0800
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipInputStream.java Sat Feb 28 13:17:13 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, 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
@@ -303,7 +303,7 @@
throw new ZipException("encrypted ZIP entry not supported");
}
e.method = get16(tmpbuf, LOCHOW);
- e.time = dosToJavaTime(get32(tmpbuf, LOCTIM));
+ e.xdostime = get32(tmpbuf, LOCTIM);
if ((flag & 8) == 8) {
/* "Data Descriptor" present */
if (e.method != DEFLATED) {
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java Sat Feb 28 10:47:07 2015 +0800
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java Sat Feb 28 13:17:13 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, 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
@@ -61,7 +61,6 @@
private static class XEntry {
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;
@@ -192,7 +191,7 @@
if (current != null) {
closeEntry(); // close previous entry
}
- if (e.time == -1) {
+ if (e.xdostime == -1) {
// by default, do NOT use extended timestamps in extra
// data, for now.
e.setTime(System.currentTimeMillis());
@@ -389,18 +388,12 @@
boolean hasZip64 = false;
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(xentry.dostime); // last modification time
+ writeInt(e.xdostime); // last modification time
// store size, uncompressed size, and crc-32 in data descriptor
// immediately following compressed entry data
writeInt(0);
@@ -415,7 +408,7 @@
}
writeShort(flag); // general purpose bit flag
writeShort(e.method); // compression method
- writeInt(xentry.dostime); // last modification time
+ writeInt(e.xdostime); // last modification time
writeInt(e.crc); // crc-32
if (hasZip64) {
writeInt(ZIP64_MAGICVAL);
@@ -522,9 +515,7 @@
}
writeShort(flag); // general purpose bit flag
writeShort(e.method); // compression method
- // use the copy in xentry, which has been converted
- // from e.time in writeLOC()
- writeInt(xentry.dostime); // last modification time
+ writeInt(e.xdostime); // last modification time
writeInt(e.crc); // crc-32
writeInt(csize); // compressed size
writeInt(size); // uncompressed size
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java Sat Feb 28 10:47:07 2015 +0800
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java Sat Feb 28 13:17:13 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -29,9 +29,6 @@
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
@@ -69,7 +66,7 @@
/**
* Converts DOS time to Java time (number of milliseconds since epoch).
*/
- public static long dosToJavaTime(long dtime) {
+ private static long dosToJavaTime(long dtime) {
@SuppressWarnings("deprecation") // Use of date constructor.
Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
(int)(((dtime >> 21) & 0x0f) - 1),
@@ -81,14 +78,26 @@
}
/**
+ * Converts extended DOS time to Java time, where up to 1999 milliseconds
+ * might be encoded into the upper half of the returned long.
+ *
+ * @param xdostime the extended DOS time value
+ * @return milliseconds since epoch
+ */
+ public static long extendedDosToJavaTime(long xdostime) {
+ long time = dosToJavaTime(xdostime);
+ return time + (xdostime >> 32);
+ }
+
+ /**
* Converts Java time to DOS time.
*/
@SuppressWarnings("deprecation") // Use of date methods
- public static long javaToDosTime(long time) {
+ private static long javaToDosTime(long time) {
Date d = new Date(time);
int year = d.getYear() + 1900;
if (year < 1980) {
- return (1 << 21) | (1 << 16);
+ return ZipEntry.DOSTIME_BEFORE_1980;
}
return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
@@ -96,6 +105,23 @@
}
/**
+ * Converts Java time to DOS time, encoding any milliseconds lost
+ * in the conversion into the upper half of the returned long.
+ *
+ * @param time milliseconds since epoch
+ * @return DOS time with 2s remainder encoded into upper half
+ */
+ public static long javaToExtendedDosTime(long time) {
+ if (time < 0) {
+ return ZipEntry.DOSTIME_BEFORE_1980;
+ }
+ long dostime = javaToDosTime(time);
+ return (dostime != ZipEntry.DOSTIME_BEFORE_1980)
+ ? dostime + ((time % 2000) << 32)
+ : ZipEntry.DOSTIME_BEFORE_1980;
+ }
+
+ /**
* Fetches unsigned 16-bit value from byte array at specified offset.
* The bytes are assumed to be in Intel (little-endian) byte order.
*/
--- a/jdk/test/java/util/zip/TestExtraTime.java Sat Feb 28 10:47:07 2015 +0800
+++ b/jdk/test/java/util/zip/TestExtraTime.java Sat Feb 28 13:17:13 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -71,6 +71,7 @@
}
testNullHandling();
+ testTimeConversions();
}
static void test(FileTime mtime, FileTime atime, FileTime ctime,
@@ -178,4 +179,33 @@
// pass
}
}
+
+ // verify that setting and getting any time is possible as per the intent
+ // of 4759491
+ static void testTimeConversions() {
+ // Sample across the entire range
+ long step = Long.MAX_VALUE / 100L;
+ testTimeConversions(Long.MIN_VALUE, Long.MAX_VALUE - step, step);
+
+ // Samples through the near future
+ long currentTime = System.currentTimeMillis();
+ testTimeConversions(currentTime, currentTime + 1_000_000, 10_000);
+ }
+
+ static void testTimeConversions(long from, long to, long step) {
+ ZipEntry ze = new ZipEntry("TestExtraTime.java");
+ for (long time = from; time <= to; time += step) {
+ ze.setTime(time);
+ FileTime lastModifiedTime = ze.getLastModifiedTime();
+ if (lastModifiedTime.toMillis() != time) {
+ throw new RuntimeException("setTime should make getLastModifiedTime " +
+ "return the specified instant: " + time +
+ " got: " + lastModifiedTime.toMillis());
+ }
+ if (ze.getTime() != time) {
+ throw new RuntimeException("getTime after setTime, expected: " +
+ time + " got: " + ze.getTime());
+ }
+ }
+ }
}