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:
parent
d57a6a4cb3
commit
9e918d6fbc
@ -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
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
120
jdk/src/share/classes/java/util/zip/ZipUtils.java
Normal file
120
jdk/src/share/classes/java/util/zip/ZipUtils.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;}
|
||||
|
79
jdk/test/java/util/zip/TestExtraTime.java
Normal file
79
jdk/test/java/util/zip/TestExtraTime.java
Normal 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!");
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user