8161942: java.util.zip.ZipEntry.java not covering UpperLimit range of DOS epoch
authorsherman
Fri, 22 Jul 2016 16:32:48 -0700
changeset 39772 c2a5d2de5253
parent 39771 ab558f578f30
child 39773 0392b8791906
8161942: java.util.zip.ZipEntry.java not covering UpperLimit range of DOS epoch Reviewed-by: redestad
jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java
jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java
jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java
jdk/test/java/util/zip/TestExtraTime.java
jdk/test/java/util/zip/TestLocalTime.java
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java	Fri Jul 22 16:05:23 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java	Fri Jul 22 16:32:48 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, 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
@@ -568,9 +568,18 @@
                     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));
+                    long wtime = get64(extra, pos + 4);
+                    if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+                        mtime = winTimeToFileTime(wtime);
+                    }
+                    wtime = get64(extra, pos + 12);
+                    if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+                        atime = winTimeToFileTime(wtime);
+                    }
+                    wtime = get64(extra, pos + 20);
+                    if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+                        ctime = winTimeToFileTime(wtime);
+                    }
                     break;
                 case EXTID_EXTT:
                     int flag = Byte.toUnsignedInt(extra[off]);
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java	Fri Jul 22 16:05:23 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java	Fri Jul 22 16:32:48 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -421,22 +421,36 @@
         byte[] nameBytes = zc.getBytes(e.name);
         writeShort(nameBytes.length);
 
-        int elenEXTT = 0;               // info-zip extended timestamp
+        int elenEXTT = 0;         // info-zip extended timestamp
         int flagEXTT = 0;
+        long umtime = -1;
+        long uatime = -1;
+        long uctime = -1;
         if (e.mtime != null) {
             elenEXTT += 4;
             flagEXTT |= EXTT_FLAG_LMT;
+            umtime = fileTimeToUnixTime(e.mtime);
         }
         if (e.atime != null) {
             elenEXTT += 4;
             flagEXTT |= EXTT_FLAG_LAT;
+            uatime = fileTimeToUnixTime(e.atime);
         }
         if (e.ctime != null) {
             elenEXTT += 4;
             flagEXTT |= EXTT_FLAT_CT;
+            uctime = fileTimeToUnixTime(e.ctime);
         }
-        if (flagEXTT != 0)
-            elen += (elenEXTT + 5);    // headid(2) + size(2) + flag(1) + data
+        if (flagEXTT != 0) {
+            // to use ntfs time if any m/a/ctime is beyond unixtime upper bound
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                elen += 36;                // NTFS time, total 36 bytes
+            } else {
+                elen += (elenEXTT + 5);    // headid(2) + size(2) + flag(1) + data
+            }
+        }
         writeShort(elen);
         writeBytes(nameBytes, 0, nameBytes.length);
         if (hasZip64) {
@@ -446,15 +460,31 @@
             writeLong(e.csize);
         }
         if (flagEXTT != 0) {
-            writeShort(EXTID_EXTT);
-            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 (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                writeShort(EXTID_NTFS);    // id
+                writeShort(32);            // data size
+                writeInt(0);               // reserved
+                writeShort(0x0001);        // NTFS attr tag
+                writeShort(24);
+                writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.mtime));
+                writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.atime));
+                writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.ctime));
+            } else {
+                writeShort(EXTID_EXTT);
+                writeShort(elenEXTT + 1);  // flag + data
+                writeByte(flagEXTT);
+                if (e.mtime != null)
+                    writeInt(umtime);
+                if (e.atime != null)
+                    writeInt(uatime);
+                if (e.ctime != null)
+                    writeInt(uctime);
+            }
         }
         writeExtra(e.extra);
         locoff = written;
@@ -528,18 +558,30 @@
         // cen info-zip extended timestamp only outputs mtime
         // but set the flag for a/ctime, if present in loc
         int flagEXTT = 0;
+        long umtime = -1;
+        long uatime = -1;
+        long uctime = -1;
         if (e.mtime != null) {
-            elen += 4;              // + mtime(4)
             flagEXTT |= EXTT_FLAG_LMT;
+            umtime = fileTimeToUnixTime(e.mtime);
         }
         if (e.atime != null) {
             flagEXTT |= EXTT_FLAG_LAT;
+            uatime = fileTimeToUnixTime(e.atime);
         }
         if (e.ctime != null) {
             flagEXTT |= EXTT_FLAT_CT;
+            uctime = fileTimeToUnixTime(e.ctime);
         }
         if (flagEXTT != 0) {
-            elen += 5;             // headid + sz + flag
+            // to use ntfs time if any m/a/ctime is beyond unixtime upper bound
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                elen += 36;         // NTFS time total 36 bytes
+            } else {
+                elen += 9;          // headid(2) + sz(2) + flag(1) + mtime (4)
+            }
         }
         writeShort(elen);
         byte[] commentBytes;
@@ -568,14 +610,30 @@
                 writeLong(xentry.offset);
         }
         if (flagEXTT != 0) {
-            writeShort(EXTID_EXTT);
-            if (e.mtime != null) {
-                writeShort(5);      // flag + mtime
-                writeByte(flagEXTT);
-                writeInt(fileTimeToUnixTime(e.mtime));
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                writeShort(EXTID_NTFS);    // id
+                writeShort(32);            // data size
+                writeInt(0);               // reserved
+                writeShort(0x0001);        // NTFS attr tag
+                writeShort(24);
+                writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.mtime));
+                writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.atime));
+                writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.ctime));
             } else {
-                writeShort(1);      // flag only
-                writeByte(flagEXTT);
+                writeShort(EXTID_EXTT);
+                if (e.mtime != null) {
+                    writeShort(5);      // flag + mtime
+                    writeByte(flagEXTT);
+                    writeInt(umtime);
+                } else {
+                    writeShort(1);      // flag only
+                    writeByte(flagEXTT);
+                }
             }
         }
         writeExtra(e.extra);
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java	Fri Jul 22 16:05:23 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java	Fri Jul 22 16:32:48 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -38,6 +38,9 @@
     // used to adjust values between Windows and java epoch
     private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
 
+    // used to indicate the corresponding windows time is not available
+    public static final long WINDOWS_TIME_NOT_AVAILABLE = Long.MIN_VALUE;
+
     /**
      * Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
      */
@@ -54,6 +57,11 @@
     }
 
     /**
+     * The upper bound of the 32-bit unix time, the "year 2038 problem".
+     */
+    public static final long UPPER_UNIXTIME_BOUND = 0x7fffffff;
+
+    /**
      * Converts "standard Unix time"(in seconds, UTC/GMT) to FileTime
      */
     public static final FileTime unixTimeToFileTime(long utime) {
--- a/jdk/test/java/util/zip/TestExtraTime.java	Fri Jul 22 16:05:23 2016 +0100
+++ b/jdk/test/java/util/zip/TestExtraTime.java	Fri Jul 22 16:32:48 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526 8130914
+ * @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526 8130914 8161942
  * @summary Test ZOS and ZIS timestamp in extra field correctly
  */
 
@@ -32,6 +32,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.FileTime;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
@@ -40,37 +41,52 @@
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
+
+
 public class TestExtraTime {
 
     public static void main(String[] args) throws Throwable{
 
         File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java");
-        if (src.exists()) {
+        if (!src.exists()) {
+            return;
+        }
+
+        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
+
+        for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) {
+
+            // ms-dos 1980 epoch problem
+            test0(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
+            // negative epoch time
+            test0(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra);
+
             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");
-
-            for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) {
-                test(mtime, null, null, null, extra);
+            test(FileTime.from(time, TimeUnit.MILLISECONDS),
+                 FileTime.from(time + 300000, TimeUnit.MILLISECONDS),
+                 FileTime.from(time - 300000, TimeUnit.MILLISECONDS),
+                 tz, extra);
 
-                // ms-dos 1980 epoch problem
-                test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
-                // negative epoch time
-                test(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra);
-
-                // non-default tz
-                test(mtime, null, null, tz, extra);
+            // now
+            time = Instant.now().toEpochMilli();
+            test(FileTime.from(time, TimeUnit.MILLISECONDS),
+                 FileTime.from(time + 300000, TimeUnit.MILLISECONDS),
+                 FileTime.from(time - 300000, TimeUnit.MILLISECONDS),
+                 tz, extra);
 
-                test(mtime, atime, null, null, extra);
-                test(mtime, null, ctime, null, extra);
-                test(mtime, atime, ctime, null, extra);
+            // unix 2038
+            time = 0x80000000L;
+            test(FileTime.from(time, TimeUnit.SECONDS),
+                 FileTime.from(time, TimeUnit.SECONDS),
+                 FileTime.from(time, TimeUnit.SECONDS),
+                 tz, extra);
 
-                test(mtime, atime, null, tz, extra);
-                test(mtime, null, ctime, tz, extra);
-                test(mtime, atime, ctime, tz, extra);
-            }
+            // mtime < unix 2038
+            time = 0x7fffffffL;
+            test(FileTime.from(time, TimeUnit.SECONDS),
+                 FileTime.from(time + 30000, TimeUnit.SECONDS),
+                 FileTime.from(time + 30000, TimeUnit.SECONDS),
+                 tz, extra);
         }
 
         testNullHandling();
@@ -80,6 +96,18 @@
 
     static void test(FileTime mtime, FileTime atime, FileTime ctime,
                      TimeZone tz, byte[] extra) throws Throwable {
+        test0(mtime, null, null, null, extra);
+        test0(mtime, null, null, tz, extra);    // non-default tz
+        test0(mtime, atime, null, null, extra);
+        test0(mtime, null, ctime, null, extra);
+        test0(mtime, atime, ctime, null, extra);
+        test0(mtime, atime, null, tz, extra);
+        test0(mtime, null, ctime, tz, extra);
+        test0(mtime, atime, ctime, tz, extra);
+    }
+
+    static void test0(FileTime mtime, FileTime atime, FileTime ctime,
+                     TimeZone tz, byte[] extra) throws Throwable {
         System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
                           mtime, atime, ctime);
         TimeZone tz0 = TimeZone.getDefault();
@@ -120,13 +148,14 @@
         Path zpath = Paths.get(System.getProperty("test.dir", "."),
                                "TestExtraTime.zip");
         Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
-        ZipFile zf = new ZipFile(zpath.toFile());
-        ze = zf.getEntry("TestExtraTime.java");
-        // ZipFile read entry from cen, which does not have a/ctime,
-        // for now.
-        check(mtime, null, null, ze, extra);
-        zf.close();
-        Files.delete(zpath);
+        try (ZipFile zf = new ZipFile(zpath.toFile())) {
+            ze = zf.getEntry("TestExtraTime.java");
+            // ZipFile read entry from cen, which does not have a/ctime,
+            // for now.
+            check(mtime, null, null, ze, extra);
+        } finally {
+            Files.delete(zpath);
+        }
     }
 
     static void check(FileTime mtime, FileTime atime, FileTime ctime,
--- a/jdk/test/java/util/zip/TestLocalTime.java	Fri Jul 22 16:05:23 2016 +0100
+++ b/jdk/test/java/util/zip/TestLocalTime.java	Fri Jul 22 16:32:48 2016 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8075526 8135108 8155616
+ * @bug 8075526 8135108 8155616 8161942
  * @summary Test timestamp via ZipEntry.get/setTimeLocal()
  */
 
@@ -65,6 +65,9 @@
             test(LocalDateTime.of(1968, 04, 28, 2, 51, 25));
             test(LocalDateTime.of(1970, 04, 26, 2, 31, 52));
 
+            // for #8161942
+            test(LocalDateTime.of(2200, 04, 26, 2, 31, 52));  // unix 2038
+
         } finally {
             TimeZone.setDefault(tz0);
         }