Mercurial > hg > shenandoah-preopenjdk-archive > openjdk8 > jdk
changeset 7267:90df6756406f
4759491: method ZipEntry.setTime(long) works incorrectly
6303183: Support NTFS and Unix-style timestamps for entries in Zip files
7012856: (zipfs) Newly created entry in zip file system should set all file times non-null values.
7012868: (zipfs) file times of entry in zipfs should always be the same regardless of TimeZone.
Summary: to add suuport of Info-ZIP extended timestamp in extra data fields
Reviewed-by: martin, alanb
line wrap: on
line diff
--- a/src/share/classes/java/util/zip/ZipConstants.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/classes/java/util/zip/ZipConstants.java Wed May 29 19:50:47 2013 -0700 @@ -69,6 +69,14 @@ static final int EXTLEN = 12; // uncompressed size /* + * 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 + + /* * Central directory (CEN) header field offsets */ static final int CENVEM = 4; // version made by
--- a/src/share/classes/java/util/zip/ZipEntry.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/classes/java/util/zip/ZipEntry.java Wed May 29 19:50:47 2013 -0700 @@ -25,8 +25,6 @@ package java.util.zip; -import java.util.Date; - /** * This class is used to represent a ZIP file entry. * @@ -35,7 +33,7 @@ public class ZipEntry implements ZipConstants, Cloneable { String name; // entry name - long time = -1; // modification time (in DOS time) + long mtime = -1; // last modification time long crc = -1; // crc-32 of entry data long size = -1; // uncompressed size of entry data long csize = -1; // compressed size of entry data @@ -79,7 +77,7 @@ */ public ZipEntry(ZipEntry e) { name = e.name; - time = e.time; + mtime = e.mtime; crc = e.crc; size = e.size; csize = e.csize; @@ -89,7 +87,7 @@ comment = e.comment; } - /* + /** * Creates a new un-initialized zip entry */ ZipEntry() {} @@ -103,22 +101,26 @@ } /** - * Sets the modification time of the entry. - * @param time the entry modification time in number of milliseconds - * since the epoch + * Sets the last modification time of the entry. + * + * @param time the last modification time of the entry in milliseconds since the epoch * @see #getTime() */ public void setTime(long time) { - this.time = javaToDosTime(time); + this.mtime = time; } /** - * Returns the modification time of the entry, or -1 if not specified. - * @return the modification time of the entry, or -1 if not specified + * 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}. + * + * @return the last modification time of the entry, or -1 if not specified * @see #setTime(long) */ public long getTime() { - return time != -1 ? dosToJavaTime(time) : -1; + return mtime; } /** @@ -277,35 +279,6 @@ return getName(); } - /* - * Converts DOS time to Java time (number of milliseconds since epoch). - */ - 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), - (int)((dtime >> 16) & 0x1f), - (int)((dtime >> 11) & 0x1f), - (int)((dtime >> 5) & 0x3f), - (int)((dtime << 1) & 0x3e)); - return d.getTime(); - } - - /* - * Converts Java time to DOS time. - */ - @SuppressWarnings("deprecation") // Use of date methods - 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 (year - 1980) << 25 | (d.getMonth() + 1) << 21 | - d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | - d.getSeconds() >> 1; - } - /** * Returns the hash code value for this entry. */
--- a/src/share/classes/java/util/zip/ZipFile.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/classes/java/util/zip/ZipFile.java Wed May 29 19:50:47 2013 -0700 @@ -46,6 +46,7 @@ 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. @@ -564,12 +565,44 @@ e.name = zc.toString(bname, bname.length); } } - e.time = getEntryTime(jzentry); e.crc = getEntryCrc(jzentry); e.size = getEntrySize(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)); + } byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); if (bcomm == null) { e.comment = null;
--- a/src/share/classes/java/util/zip/ZipInputStream.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/classes/java/util/zip/ZipInputStream.java Wed May 29 19:50:47 2013 -0700 @@ -32,6 +32,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import static java.util.zip.ZipConstants64.*; +import static java.util.zip.ZipUtils.*; /** * This class implements an input stream filter for reading files in the @@ -302,7 +303,7 @@ throw new ZipException("encrypted ZIP entry not supported"); } e.method = get16(tmpbuf, LOCHOW); - e.time = get32(tmpbuf, LOCTIM); + e.mtime = dosToJavaTime(get32(tmpbuf, LOCTIM)); if ((flag & 8) == 8) { /* "Data Descriptor" present */ if (e.method != DEFLATED) { @@ -316,32 +317,51 @@ } len = get16(tmpbuf, LOCEXT); if (len > 0) { - byte[] bb = new byte[len]; - readFully(bb, 0, len); - e.setExtra(bb); + byte[] extra = new byte[len]; + readFully(extra, 0, len); + e.setExtra(extra); // extra fields are in "HeaderID(2)DataSize(2)Data... format - if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) { - int off = 0; - while (off + 4 < len) { - int sz = get16(bb, off + 2); - if (get16(bb, off) == ZIP64_EXTID) { - off += 4; - // LOC extra zip64 entry MUST include BOTH original and - // compressed file size fields - if (sz < 16 || (off + sz) > len ) { - // Invalid zip64 extra fields, simply skip. Even it's - // rare, it's possible the entry size happens to be - // the magic value and it "accidnetly" has some bytes - // in extra match the id. - return e; - } - e.size = get64(bb, off); - e.csize = get64(bb, off + 8); + 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; } - off += (sz + 4); + break; + default: // unknown tag } + off += (sz + 4); } + } return e; } @@ -430,27 +450,4 @@ } } - /* - * Fetches unsigned 16-bit value from byte array at specified offset. - * The bytes are assumed to be in Intel (little-endian) byte order. - */ - private static final int get16(byte b[], int off) { - return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); - } - - /* - * Fetches unsigned 32-bit value from byte array at specified offset. - * The bytes are assumed to be in Intel (little-endian) byte order. - */ - private static final long get32(byte b[], int off) { - return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; - } - - /* - * Fetches signed 64-bit value from byte array at specified offset. - * The bytes are assumed to be in Intel (little-endian) byte order. - */ - private static final long get64(byte b[], int off) { - return get32(b, off) | (get32(b, off+4) << 32); - } }
--- a/src/share/classes/java/util/zip/ZipOutputStream.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/classes/java/util/zip/ZipOutputStream.java Wed May 29 19:50:47 2013 -0700 @@ -32,6 +32,7 @@ import java.util.Vector; import java.util.HashSet; import static java.util.zip.ZipConstants64.*; +import static java.util.zip.ZipUtils.*; /** * This class implements an output stream filter for writing files in the @@ -190,7 +191,7 @@ if (current != null) { closeEntry(); // close previous entry } - if (e.time == -1) { + if (e.mtime == -1) { e.setTime(System.currentTimeMillis()); } if (e.method == -1) { @@ -382,16 +383,25 @@ 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; - boolean hasZip64 = false; - + 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); + } 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(e.time); // last modification time + writeInt(javaToDosTime(e.mtime)); // last modification time // store size, uncompressed size, and crc-32 in data descriptor // immediately following compressed entry data @@ -407,7 +417,7 @@ } writeShort(flag); // general purpose bit flag writeShort(e.method); // compression method - writeInt(e.time); // last modification time + writeInt(javaToDosTime(e.mtime)); // last modification time writeInt(e.crc); // crc-32 if (hasZip64) { writeInt(ZIP64_MAGICVAL); @@ -420,6 +430,8 @@ } byte[] nameBytes = zc.getBytes(e.name); writeShort(nameBytes.length); + if (!foundEXTT) + elen += 9; // use Info-ZIP's ext time in extra writeShort(elen); writeBytes(nameBytes, 0, nameBytes.length); if (hasZip64) { @@ -428,6 +440,12 @@ writeLong(e.size); writeLong(e.csize); } + if (!foundEXTT) { + 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); } @@ -457,25 +475,25 @@ ZipEntry e = xentry.entry; int flag = e.flag; int version = version(e); - long csize = e.csize; long size = e.size; long offset = xentry.offset; - int e64len = 0; + int elenZIP64 = 0; boolean hasZip64 = false; + if (e.csize >= ZIP64_MAGICVAL) { csize = ZIP64_MAGICVAL; - e64len += 8; // csize(8) + elenZIP64 += 8; // csize(8) hasZip64 = true; } if (e.size >= ZIP64_MAGICVAL) { size = ZIP64_MAGICVAL; // size(8) - e64len += 8; + elenZIP64 += 8; hasZip64 = true; } if (xentry.offset >= ZIP64_MAGICVAL) { offset = ZIP64_MAGICVAL; - e64len += 8; // offset(8) + elenZIP64 += 8; // offset(8) hasZip64 = true; } writeInt(CENSIG); // CEN header signature @@ -488,18 +506,32 @@ } writeShort(flag); // general purpose bit flag writeShort(e.method); // compression method - writeInt(e.time); // last modification time + writeInt(javaToDosTime(e.mtime)); // 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); + } if (hasZip64) { // + headid(2) + datasize(2) - writeShort(e64len + 4 + (e.extra != null ? e.extra.length : 0)); - } else { - writeShort(e.extra != null ? e.extra.length : 0); + elen += (elenZIP64 + 4); } + if (!foundEXTT) + elen += 9; // Info-ZIP's Extended Timestamp + writeShort(elen); byte[] commentBytes; if (e.comment != null) { commentBytes = zc.getBytes(e.comment); @@ -515,7 +547,7 @@ writeBytes(nameBytes, 0, nameBytes.length); if (hasZip64) { writeShort(ZIP64_EXTID);// Zip64 extra - writeShort(e64len); + writeShort(elenZIP64); if (size == ZIP64_MAGICVAL) writeLong(e.size); if (csize == ZIP64_MAGICVAL) @@ -523,6 +555,12 @@ if (offset == ZIP64_MAGICVAL) writeLong(xentry.offset); } + if (!foundEXTT) { + writeShort(EXTID_EXTT); + writeShort(5); + writeByte(0x1); // flags byte + writeInt(javaToUnixTime(e.mtime)); + } if (e.extra != null) { writeBytes(e.extra, 0, e.extra.length); } @@ -589,6 +627,15 @@ } /* + * Writes a 8-bit byte to the output stream. + */ + private void writeByte(int v) throws IOException { + OutputStream out = this.out; + out.write(v & 0xff); + written += 1; + } + + /* * Writes a 16-bit short to the output stream in little-endian byte order. */ private void writeShort(int v) throws IOException {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/util/zip/ZipUtils.java Wed May 29 19:50:47 2013 -0700 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +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. + */ + public static final long winToJavaTime(long wtime) { + return TimeUnit.MILLISECONDS.convert( + wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS); + } + + /** + * Converts Java time to Windows time. + */ + public static final long javaToWinTime(long time) { + return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS) + - WINDOWS_EPOCH_IN_MICROSECONDS) * 10; + } + + /** + * Converts "standard Unix time"(in seconds, UTC/GMT) to Java time + */ + public static final long unixToJavaTime(long utime) { + return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS); + } + + /** + * Converts Java time to "standard Unix time". + */ + public static final long javaToUnixTime(long time) { + return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS); + } + + /** + * Converts DOS time to Java time (number of milliseconds since epoch). + */ + public 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), + (int)((dtime >> 16) & 0x1f), + (int)((dtime >> 11) & 0x1f), + (int)((dtime >> 5) & 0x3f), + (int)((dtime << 1) & 0x3e)); + return d.getTime(); + } + + /** + * Converts Java time to DOS time. + */ + @SuppressWarnings("deprecation") // Use of date methods + public static long javaToDosTime(long time) { + Date d = new Date(time); + int year = d.getYear() + 1900; + if (year < 1980) { + return (1 << 21) | (1 << 16); + } + return (year - 1980) << 25 | (d.getMonth() + 1) << 21 | + d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | + 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. + */ + public static final int get16(byte b[], int off) { + return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); + } + + /** + * Fetches unsigned 32-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + public static final long get32(byte b[], int off) { + return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + } + + /** + * Fetches signed 64-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + public static final long get64(byte b[], int off) { + return get32(b, off) | (get32(b, off+4) << 32); + } + +}
--- a/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java Wed May 29 19:50:47 2013 -0700 @@ -1818,7 +1818,7 @@ Entry(byte[] name) { name(name); - this.mtime = System.currentTimeMillis(); + this.mtime = this.ctime = this.atime = System.currentTimeMillis(); this.crc = 0; this.size = 0; this.csize = 0; @@ -1912,17 +1912,18 @@ { int written = CENHDR; int version0 = version(); - long csize0 = csize; long size0 = size; long locoff0 = locoff; int elen64 = 0; // extra for ZIP64 int elenNTFS = 0; // extra for NTFS (a/c/mtime) int elenEXTT = 0; // extra for Extended Timestamp + boolean foundExtraTime = false; // if time stamp NTFS, EXTT present // confirm size/length int nlen = (name != null) ? name.length : 0; int elen = (extra != null) ? extra.length : 0; + int eoff = 0; int clen = (comment != null) ? comment.length : 0; if (csize >= ZIP64_MINVAL) { csize0 = ZIP64_MINVAL; @@ -1936,14 +1937,24 @@ locoff0 = ZIP64_MINVAL; elen64 += 8; // offset(8) } - if (elen64 != 0) + if (elen64 != 0) { elen64 += 4; // header and data sz 4 bytes + } - if (atime != -1) { - if (isWindows) // use NTFS + while (eoff + 4 < elen) { + int tag = SH(extra, eoff); + int sz = SH(extra, eoff + 2); + if (tag == EXTID_EXTT || tag == EXTID_NTFS) { + foundExtraTime = true; + } + eoff += (4 + sz); + } + if (!foundExtraTime) { + if (isWindows) { // use NTFS elenNTFS = 36; // total 36 bytes - else // Extended Timestamp otherwise + } else { // Extended Timestamp otherwise elenEXTT = 9; // only mtime in cen + } } writeInt(os, CENSIG); // CEN header signature if (elen64 != 0) { @@ -2092,11 +2103,13 @@ { writeInt(os, LOCSIG); // LOC header signature int version = version(); - int nlen = (name != null) ? name.length : 0; int elen = (extra != null) ? extra.length : 0; + boolean foundExtraTime = false; // if extra timestamp present + int eoff = 0; int elen64 = 0; int elenEXTT = 0; + int elenNTFS = 0; if ((flag & FLAG_DATADESCR) != 0) { writeShort(os, version()); // version needed to extract writeShort(os, flag); // general purpose bit flag @@ -2128,14 +2141,27 @@ writeInt(os, size); // uncompressed size } } - if (atime != -1 && !isWindows) { // on unix use "ext time" - if (ctime == -1) - elenEXTT = 13; - else - elenEXTT = 17; + while (eoff + 4 < elen) { + int tag = SH(extra, eoff); + int sz = SH(extra, eoff + 2); + if (tag == EXTID_EXTT || tag == EXTID_NTFS) { + foundExtraTime = true; + } + eoff += (4 + sz); + } + if (!foundExtraTime) { + if (isWindows) { + elenNTFS = 36; // NTFS, total 36 bytes + } else { // on unix use "ext time" + elenEXTT = 9; + if (atime != -1) + elenEXTT += 4; + if (ctime != -1) + elenEXTT += 4; + } } writeShort(os, name.length); - writeShort(os, elen + elen64 + elenEXTT); + writeShort(os, elen + elen64 + elenNTFS + elenEXTT); writeBytes(os, name); if (elen64 != 0) { writeShort(os, EXTID_ZIP64); @@ -2143,15 +2169,28 @@ writeLong(os, size); writeLong(os, csize); } + if (elenNTFS != 0) { + writeShort(os, EXTID_NTFS); + writeShort(os, elenNTFS - 4); + writeInt(os, 0); // reserved + writeShort(os, 0x0001); // NTFS attr tag + writeShort(os, 24); + writeLong(os, javaToWinTime(mtime)); + writeLong(os, javaToWinTime(atime)); + writeLong(os, javaToWinTime(ctime)); + } if (elenEXTT != 0) { writeShort(os, EXTID_EXTT); writeShort(os, elenEXTT - 4);// size for the folowing data block - if (ctime == -1) - os.write(0x3); // mtime and atime - else - os.write(0x7); // mtime, atime and ctime + int fbyte = 0x1; + if (atime != -1) // mtime and atime + fbyte |= 0x2; + if (ctime != -1) // mtime, atime and ctime + fbyte |= 0x4; + os.write(fbyte); // flags byte writeInt(os, javaToUnixTime(mtime)); - writeInt(os, javaToUnixTime(atime)); + if (atime != -1) + writeInt(os, javaToUnixTime(atime)); if (ctime != -1) writeInt(os, javaToUnixTime(ctime)); }
--- a/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipInfo.java Wed May 29 14:57:51 2013 +0100 +++ b/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipInfo.java Wed May 29 19:50:47 2013 -0700 @@ -214,7 +214,7 @@ winToJavaTime(LL(extra, off + 24))); break; case EXTID_EXTT: - print(" ->Inof-ZIP Extended Timestamp: flag=%x%n",extra[off]); + print(" ->Info-ZIP Extended Timestamp: flag=%x%n",extra[off]); pos = off + 1 ; while (pos + 4 <= off + sz) { print(" *%tc%n", @@ -223,6 +223,7 @@ } break; default: + print(" ->[tag=%x, size=%d]%n", tag, sz); } off += sz; }
--- a/test/demo/zipfs/ZipFSTester.java Wed May 29 14:57:51 2013 +0100 +++ b/test/demo/zipfs/ZipFSTester.java Wed May 29 19:50:47 2013 -0700 @@ -29,6 +29,7 @@ import java.nio.file.attribute.*; import java.net.*; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.zip.*; import static java.nio.file.StandardOpenOption.*; @@ -48,6 +49,7 @@ test0(fs); test1(fs); test2(fs); // more tests + testTime(Paths.get(args[0])); } } @@ -337,6 +339,46 @@ Files.delete(fs3Path); } + // test file stamp + static void testTime(Path src) throws Exception { + // create a new filesystem, copy this file into it + Map<String, Object> env = new HashMap<String, Object>(); + env.put("create", "true"); + Path fsPath = getTempPath(); + FileSystem fs = newZipFileSystem(fsPath, env); + + System.out.println("test copy with timestamps..."); + // copyin + Path dst = getPathWithParents(fs, "me"); + Files.copy(src, dst, COPY_ATTRIBUTES); + checkEqual(src, dst); + + BasicFileAttributes attrs = Files + .getFileAttributeView(src, BasicFileAttributeView.class) + .readAttributes(); + System.out.println("mtime: " + attrs.lastModifiedTime()); + System.out.println("ctime: " + attrs.creationTime()); + System.out.println("atime: " + attrs.lastAccessTime()); + System.out.println(" ==============>"); + BasicFileAttributes dstAttrs = Files + .getFileAttributeView(dst, BasicFileAttributeView.class) + .readAttributes(); + System.out.println("mtime: " + dstAttrs.lastModifiedTime()); + System.out.println("ctime: " + dstAttrs.creationTime()); + System.out.println("atime: " + dstAttrs.lastAccessTime()); + + // 1-second granularity + if (attrs.lastModifiedTime().to(TimeUnit.SECONDS) != + dstAttrs.lastModifiedTime().to(TimeUnit.SECONDS) || + attrs.lastAccessTime().to(TimeUnit.SECONDS) != + dstAttrs.lastAccessTime().to(TimeUnit.SECONDS) || + attrs.creationTime().to(TimeUnit.SECONDS) != + dstAttrs.creationTime().to(TimeUnit.SECONDS)) { + throw new RuntimeException("Timestamp Copy Failed!"); + } + Files.delete(fsPath); + } + private static FileSystem newZipFileSystem(Path path, Map<String, ?> env) throws Exception {
--- a/test/demo/zipfs/basic.sh Wed May 29 14:57:51 2013 +0100 +++ b/test/demo/zipfs/basic.sh Wed May 29 19:50:47 2013 -0700 @@ -22,7 +22,7 @@ # # @test # @bug 6990846 7009092 7009085 7015391 7014948 7005986 7017840 7007596 -# 7157656 8002390 +# 7157656 8002390 7012868 7012856 # @summary Test ZipFileSystem demo # @build Basic PathOps ZipFSTester # @run shell basic.sh
--- a/test/java/util/jar/TestExtra.java Wed May 29 14:57:51 2013 +0100 +++ b/test/java/util/jar/TestExtra.java Wed May 29 19:50:47 2013 -0700 @@ -23,7 +23,7 @@ /** * @test - * @bug 6480504 + * @bug 6480504 6303183 * @summary Test that client-provided data in the extra field is written and * read correctly, taking into account the JAR_MAGIC written into the extra * field of the first entry of JAR files. @@ -117,8 +117,7 @@ ZipInputStream zis = getInputStream(); ze = zis.getNextEntry(); - byte[] e = ze.getExtra(); - check(e.length == 8, "expected extra length is 8, got " + e.length); + checkExtra(data, ze.getExtra()); checkEntry(ze, 0, 0); } @@ -140,10 +139,43 @@ ZipInputStream zis = getInputStream(); ze = zis.getNextEntry(); byte[] e = ze.getExtra(); - check(e.length == 8, "expected extra length is 8, got " + e.length); + checkExtra(data, ze.getExtra()); checkEntry(ze, 0, 0); } + // check if all "expected" extra fields equal to their + // corresponding fields in "extra". The "extra" might have + // timestamp fields added by ZOS. + static void checkExtra(byte[] expected, byte[] extra) { + if (expected == null) + return; + int off = 0; + int len = expected.length; + while (off + 4 < len) { + int tag = get16(expected, off); + int sz = get16(expected, off + 2); + int off0 = 0; + int len0 = extra.length; + boolean matched = false; + while (off0 + 4 < len0) { + int tag0 = get16(extra, off0); + int sz0 = get16(extra, off0 + 2); + if (tag == tag0 && sz == sz0) { + matched = true; + for (int i = 0; i < sz; i++) { + if (expected[off + i] != extra[off0 +i]) + matched = false; + } + break; + } + off0 += (4 + sz0); + } + if (!matched) { + fail("Expected extra data [tag=" + tag + "sz=" + sz + "] check failed"); + } + off += (4 + sz); + } + } /** Check that the entry's extra data is correct. */ void checkEntry(ZipEntry ze, int count, int dataLength) {
--- a/test/java/util/zip/StoredCRC.java Wed May 29 14:57:51 2013 +0100 +++ b/test/java/util/zip/StoredCRC.java Wed May 29 19:50:47 2013 -0700 @@ -77,9 +77,9 @@ unexpected(t); } - // Test that data corruption is detected. Offset 39 was + // Test that data corruption is detected. "offset" was // determined to be in the entry's uncompressed data. - data[39] ^= 1; + data[getDataOffset(data) + 4] ^= 1; zis = new ZipInputStream( new ByteArrayInputStream(data)); @@ -97,6 +97,15 @@ } } + public static final int getDataOffset(byte b[]) { + final int LOCHDR = 30; // LOC header size + final int LOCEXT = 28; // extra field length + final int LOCNAM = 26; // filename length + int lenExt = Byte.toUnsignedInt(b[LOCEXT]) | (Byte.toUnsignedInt(b[LOCEXT + 1]) << 8); + int lenNam = Byte.toUnsignedInt(b[LOCNAM]) | (Byte.toUnsignedInt(b[LOCNAM + 1]) << 8); + return LOCHDR + lenExt + lenNam; + } + //--------------------- Infrastructure --------------------------- static volatile int passed = 0, failed = 0; static boolean pass() {passed++; return true;}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/zip/TestExtraTime.java Wed May 29 19:50:47 2013 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 4759491 6303183 7012868 + * @summary Test ZOS and ZIS timestamp in extra field correctly + */ + +import java.io.*; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; +import java.util.zip.ZipEntry; +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()) { + long mtime = src.lastModified(); + test(mtime, null); + test(10, null); // ms-dos 1980 epoch problem + test(mtime, TimeZone.getTimeZone("Asia/Shanghai")); + } + } + + private static void test(long mtime, TimeZone tz) throws Throwable { + TimeZone tz0 = TimeZone.getDefault(); + if (tz != null) { + TimeZone.setDefault(tz); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos); + ZipEntry ze = new ZipEntry("TestExtreTime.java"); + + ze.setTime(mtime); + zos.putNextEntry(ze); + zos.write(new byte[] { 1,2 ,3, 4}); + zos.close(); + if (tz != null) { + TimeZone.setDefault(tz0); + } + ZipInputStream zis = new ZipInputStream( + new ByteArrayInputStream(baos.toByteArray())); + ze = zis.getNextEntry(); + zis.close(); + + System.out.printf("%tc => %tc%n", mtime, ze.getTime()); + + if (TimeUnit.MILLISECONDS.toSeconds(mtime) != + TimeUnit.MILLISECONDS.toSeconds(ze.getTime())) + throw new RuntimeException("Timestamp storing failed!"); + + } +}
--- a/test/java/util/zip/ZipFile/Assortment.java Wed May 29 14:57:51 2013 +0100 +++ b/test/java/util/zip/ZipFile/Assortment.java Wed May 29 19:50:47 2013 -0700 @@ -22,7 +22,7 @@ */ /* @test - * @bug 4770745 6234507 + * @bug 4770745 6234507 6303183 * @summary test a variety of zip file entries * @author Martin Buchholz */ @@ -54,6 +54,44 @@ check(condition, "Something's wrong"); } + static final int get16(byte b[], int off) { + return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); + } + + // check if all "expected" extra fields equal to their + // corresponding fields in "extra". The "extra" might have + // timestamp fields added by ZOS. + static boolean equalsExtraData(byte[] expected, byte[] extra) { + if (expected == null) + return true; + int off = 0; + int len = expected.length; + while (off + 4 < len) { + int tag = get16(expected, off); + int sz = get16(expected, off + 2); + int off0 = 0; + int len0 = extra.length; + boolean matched = false; + while (off0 + 4 < len0) { + int tag0 = get16(extra, off0); + int sz0 = get16(extra, off0 + 2); + if (tag == tag0 && sz == sz0) { + matched = true; + for (int i = 0; i < sz; i++) { + if (expected[off + i] != extra[off0 +i]) + matched = false; + } + break; + } + off0 += (4 + sz0); + } + if (!matched) + return false; + off += (4 + sz); + } + return true; + } + private static class Entry { private String name; private int method; @@ -109,7 +147,7 @@ check((((comment == null) || comment.equals("")) && (e.getComment() == null)) || comment.equals(e.getComment())); - check(Arrays.equals(extra, e.getExtra())); + check(equalsExtraData(extra, e.getExtra())); check(Arrays.equals(data, getData(f, e))); check(e.getSize() == data.length); check((method == ZipEntry.DEFLATED) || @@ -129,8 +167,7 @@ byte[] extra = (this.extra != null && this.extra.length == 0) ? null : this.extra; - check(Arrays.equals(extra, e.getExtra())); - + check(equalsExtraData(extra, e.getExtra())); check(name.equals(e.getName())); check(method == e.getMethod()); check(e.getSize() == -1 || e.getSize() == data.length);