src/share/classes/java/util/zip/ZipOutputStream.java

Print this page

        

@@ -57,12 +57,13 @@
             java.security.AccessController.doPrivileged(
                 new sun.security.action.GetPropertyAction(
                     "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;
         }
     }

@@ -189,11 +190,13 @@
     public void putNextEntry(ZipEntry e) throws IOException {
         ensureOpen();
         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) {
             e.method = method;  // use default method
         }

@@ -382,29 +385,24 @@
      */
     private void writeLOC(XEntry xentry) throws IOException {
         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);
             writeInt(0);
             writeInt(0);

@@ -415,11 +413,11 @@
             } else {
                 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
             writeInt(e.crc);            // crc-32
             if (hasZip64) {
                 writeInt(ZIP64_MAGICVAL);
                 writeInt(ZIP64_MAGICVAL);
                 elen += 20;        //headid(2) + size(2) + size(8) + csize(8)

@@ -428,29 +426,47 @@
                 writeInt(e.size);   // uncompressed size
             }
         }
         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) {
             writeShort(ZIP64_EXTID);
             writeShort(16);
             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;
     }
 
     /*
      * Writes extra data descriptor (EXT) for specified entry.

@@ -504,35 +520,39 @@
             writeShort(version);    // version made by
             writeShort(version);    // version needed to extract
         }
         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;
+        int elen = getExtraLen(e.extra);
+        if (hasZip64) {
+            elen += (elenZIP64 + 4);// + headid(2) + datasize(2)
             }
-            eoff += (4 + sz);
+        // 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 (hasZip64) {
-            // + headid(2) + datasize(2)
-            elen += (elenZIP64 + 4);
+        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) {
             commentBytes = zc.getBytes(e.comment);
             writeShort(Math.min(commentBytes.length, 0xffff));

@@ -543,29 +563,34 @@
         writeShort(0);              // starting disk number
         writeShort(0);              // internal file attributes (unused)
         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);
             if (size == ZIP64_MAGICVAL)
                 writeLong(e.size);
             if (csize == ZIP64_MAGICVAL)
                 writeLong(e.csize);
             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));
         }
     }
 

@@ -625,10 +650,51 @@
             writeShort(0);
         }
     }
 
     /*
+     * 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 {
         OutputStream out = this.out;
         out.write(v & 0xff);