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

To add suuport of Info-ZIP extended timestamp in extra data fields

Reviewed-by: martin, alanb
This commit is contained in:
Xueming Shen 2013-05-29 19:50:47 -07:00
parent d57a6a4cb3
commit 9e918d6fbc
14 changed files with 551 additions and 134 deletions

View File

@ -68,6 +68,14 @@ interface ZipConstants {
static final int EXTSIZ = 8; // compressed size
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
*/

View File

@ -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 @@ import java.util.Date;
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 @@ class ZipEntry implements ZipConstants, Cloneable {
*/
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 @@ class ZipEntry implements ZipConstants, Cloneable {
comment = e.comment;
}
/*
/**
* Creates a new un-initialized zip entry
*/
ZipEntry() {}
@ -103,22 +101,26 @@ class ZipEntry implements ZipConstants, Cloneable {
}
/**
* 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 @@ class ZipEntry implements ZipConstants, Cloneable {
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.
*/

View File

@ -46,6 +46,7 @@ import java.util.stream.Stream;
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 @@ class ZipFile implements ZipConstants, Closeable {
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;

View File

@ -32,6 +32,7 @@ import java.io.PushbackInputStream;
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 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
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 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
}
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);
break;
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);
}
off += (sz + 4);
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);
}
}
return e;
}
@ -430,27 +450,4 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
}
}
/*
* 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);
}
}

View File

@ -32,6 +32,7 @@ import java.nio.charset.StandardCharsets;
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = e.flag;
int elen = (e.extra != null) ? e.extra.length : 0;
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);
}
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
}
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
}
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
}
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
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 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
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);
}
@ -588,6 +626,15 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
}
}
/*
* 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.
*/

View File

@ -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);
}
}

View File

@ -1818,7 +1818,7 @@ public class ZipFileSystem extends FileSystem {
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 @@ public class ZipFileSystem extends FileSystem {
{
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 @@ public class ZipFileSystem extends FileSystem {
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 @@ public class ZipFileSystem extends FileSystem {
{
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 @@ public class ZipFileSystem extends FileSystem {
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 @@ public class ZipFileSystem extends FileSystem {
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));
}

View File

@ -214,7 +214,7 @@ public class ZipInfo {
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 @@ public class ZipInfo {
}
break;
default:
print(" ->[tag=%x, size=%d]%n", tag, sz);
}
off += sz;
}

View File

@ -29,6 +29,7 @@ import java.nio.file.spi.*;
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 @@ public class ZipFSTester {
test0(fs);
test1(fs);
test2(fs); // more tests
testTime(Paths.get(args[0]));
}
}
@ -337,6 +339,46 @@ public class ZipFSTester {
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
{

View File

@ -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

View File

@ -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 @@ public class TestExtra {
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 @@ public class TestExtra {
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) {

View File

@ -77,9 +77,9 @@ public class StoredCRC {
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 class StoredCRC {
}
}
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;}

View File

@ -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!");
}
}

View File

@ -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 @@ public class Assortment {
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 @@ public class Assortment {
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 @@ public class Assortment {
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);