mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
808
src/java.base/share/classes/java/util/zip/ZipOutputStream.java
Normal file
808
src/java.base/share/classes/java/util/zip/ZipOutputStream.java
Normal file
|
@ -0,0 +1,808 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* 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.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
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.*;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* This class implements an output stream filter for writing files in the
|
||||
* ZIP file format. Includes support for both compressed and uncompressed
|
||||
* entries.
|
||||
*
|
||||
* @author David Connelly
|
||||
* @since 1.1
|
||||
*/
|
||||
public
|
||||
class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
|
||||
|
||||
/**
|
||||
* Whether to use ZIP64 for zip files with more than 64k entries.
|
||||
* Until ZIP64 support in zip implementations is ubiquitous, this
|
||||
* system property allows the creation of zip files which can be
|
||||
* read by legacy zip implementations which tolerate "incorrect"
|
||||
* total entry count fields, such as the ones in jdk6, and even
|
||||
* some in jdk7.
|
||||
*/
|
||||
private static final boolean inhibitZip64 =
|
||||
Boolean.parseBoolean(
|
||||
GetPropertyAction.privilegedGetProperty("jdk.util.zip.inhibitZip64"));
|
||||
|
||||
private static class XEntry {
|
||||
final ZipEntry entry;
|
||||
final long offset;
|
||||
public XEntry(ZipEntry entry, long offset) {
|
||||
this.entry = entry;
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
private XEntry current;
|
||||
private Vector<XEntry> xentries = new Vector<>();
|
||||
private HashSet<String> names = new HashSet<>();
|
||||
private CRC32 crc = new CRC32();
|
||||
private long written = 0;
|
||||
private long locoff = 0;
|
||||
private byte[] comment;
|
||||
private int method = DEFLATED;
|
||||
private boolean finished;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
private final ZipCoder zc;
|
||||
|
||||
private static int version(ZipEntry e) throws ZipException {
|
||||
switch (e.method) {
|
||||
case DEFLATED: return 20;
|
||||
case STORED: return 10;
|
||||
default: throw new ZipException("unsupported compression method");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure that this stream has not been closed.
|
||||
*/
|
||||
private void ensureOpen() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compression method for uncompressed (STORED) entries.
|
||||
*/
|
||||
public static final int STORED = ZipEntry.STORED;
|
||||
|
||||
/**
|
||||
* Compression method for compressed (DEFLATED) entries.
|
||||
*/
|
||||
public static final int DEFLATED = ZipEntry.DEFLATED;
|
||||
|
||||
/**
|
||||
* Creates a new ZIP output stream.
|
||||
*
|
||||
* <p>The UTF-8 {@link java.nio.charset.Charset charset} is used
|
||||
* to encode the entry names and comments.
|
||||
*
|
||||
* @param out the actual output stream
|
||||
*/
|
||||
public ZipOutputStream(OutputStream out) {
|
||||
this(out, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ZIP output stream.
|
||||
*
|
||||
* @param out the actual output stream
|
||||
*
|
||||
* @param charset the {@linkplain java.nio.charset.Charset charset}
|
||||
* to be used to encode the entry names and comments
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public ZipOutputStream(OutputStream out, Charset charset) {
|
||||
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
|
||||
if (charset == null)
|
||||
throw new NullPointerException("charset is null");
|
||||
this.zc = ZipCoder.get(charset);
|
||||
usesDefaultDeflater = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ZIP file comment.
|
||||
* @param comment the comment string
|
||||
* @exception IllegalArgumentException if the length of the specified
|
||||
* ZIP file comment is greater than 0xFFFF bytes
|
||||
*/
|
||||
public void setComment(String comment) {
|
||||
if (comment != null) {
|
||||
this.comment = zc.getBytes(comment);
|
||||
if (this.comment.length > 0xffff)
|
||||
throw new IllegalArgumentException("ZIP file comment too long.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default compression method for subsequent entries. This
|
||||
* default will be used whenever the compression method is not specified
|
||||
* for an individual ZIP file entry, and is initially set to DEFLATED.
|
||||
* @param method the default compression method
|
||||
* @exception IllegalArgumentException if the specified compression method
|
||||
* is invalid
|
||||
*/
|
||||
public void setMethod(int method) {
|
||||
if (method != DEFLATED && method != STORED) {
|
||||
throw new IllegalArgumentException("invalid compression method");
|
||||
}
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compression level for subsequent entries which are DEFLATED.
|
||||
* The default setting is DEFAULT_COMPRESSION.
|
||||
* @param level the compression level (0-9)
|
||||
* @exception IllegalArgumentException if the compression level is invalid
|
||||
*/
|
||||
public void setLevel(int level) {
|
||||
def.setLevel(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins writing a new ZIP file entry and positions the stream to the
|
||||
* start of the entry data. Closes the current entry if still active.
|
||||
* The default compression method will be used if no compression method
|
||||
* was specified for the entry, and the current time will be used if
|
||||
* the entry has no set modification time.
|
||||
* @param e the ZIP entry to be written
|
||||
* @exception ZipException if a ZIP format error has occurred
|
||||
* @exception IOException if an I/O error has occurred
|
||||
*/
|
||||
public void putNextEntry(ZipEntry e) throws IOException {
|
||||
ensureOpen();
|
||||
if (current != null) {
|
||||
closeEntry(); // close previous entry
|
||||
}
|
||||
if (e.xdostime == -1) {
|
||||
// by default, do NOT use extended timestamps in extra
|
||||
// data, for now.
|
||||
e.setTime(System.currentTimeMillis());
|
||||
}
|
||||
if (e.method == -1) {
|
||||
e.method = method; // use default method
|
||||
}
|
||||
// store size, compressed size, and crc-32 in LOC header
|
||||
e.flag = 0;
|
||||
switch (e.method) {
|
||||
case DEFLATED:
|
||||
// store size, compressed size, and crc-32 in data descriptor
|
||||
// immediately following the compressed entry data
|
||||
if (e.size == -1 || e.csize == -1 || e.crc == -1)
|
||||
e.flag = 8;
|
||||
|
||||
break;
|
||||
case STORED:
|
||||
// compressed size, uncompressed size, and crc-32 must all be
|
||||
// set for entries using STORED compression method
|
||||
if (e.size == -1) {
|
||||
e.size = e.csize;
|
||||
} else if (e.csize == -1) {
|
||||
e.csize = e.size;
|
||||
} else if (e.size != e.csize) {
|
||||
throw new ZipException(
|
||||
"STORED entry where compressed != uncompressed size");
|
||||
}
|
||||
if (e.size == -1 || e.crc == -1) {
|
||||
throw new ZipException(
|
||||
"STORED entry missing size, compressed size, or crc-32");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("unsupported compression method");
|
||||
}
|
||||
if (! names.add(e.name)) {
|
||||
throw new ZipException("duplicate entry: " + e.name);
|
||||
}
|
||||
if (zc.isUTF8())
|
||||
e.flag |= EFS;
|
||||
current = new XEntry(e, written);
|
||||
xentries.add(current);
|
||||
writeLOC(current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current ZIP entry and positions the stream for writing
|
||||
* the next entry.
|
||||
* @exception ZipException if a ZIP format error has occurred
|
||||
* @exception IOException if an I/O error has occurred
|
||||
*/
|
||||
public void closeEntry() throws IOException {
|
||||
ensureOpen();
|
||||
if (current != null) {
|
||||
ZipEntry e = current.entry;
|
||||
switch (e.method) {
|
||||
case DEFLATED:
|
||||
def.finish();
|
||||
while (!def.finished()) {
|
||||
deflate();
|
||||
}
|
||||
if ((e.flag & 8) == 0) {
|
||||
// verify size, compressed size, and crc-32 settings
|
||||
if (e.size != def.getBytesRead()) {
|
||||
throw new ZipException(
|
||||
"invalid entry size (expected " + e.size +
|
||||
" but got " + def.getBytesRead() + " bytes)");
|
||||
}
|
||||
if (e.csize != def.getBytesWritten()) {
|
||||
throw new ZipException(
|
||||
"invalid entry compressed size (expected " +
|
||||
e.csize + " but got " + def.getBytesWritten() + " bytes)");
|
||||
}
|
||||
if (e.crc != crc.getValue()) {
|
||||
throw new ZipException(
|
||||
"invalid entry CRC-32 (expected 0x" +
|
||||
Long.toHexString(e.crc) + " but got 0x" +
|
||||
Long.toHexString(crc.getValue()) + ")");
|
||||
}
|
||||
} else {
|
||||
e.size = def.getBytesRead();
|
||||
e.csize = def.getBytesWritten();
|
||||
e.crc = crc.getValue();
|
||||
writeEXT(e);
|
||||
}
|
||||
def.reset();
|
||||
written += e.csize;
|
||||
break;
|
||||
case STORED:
|
||||
// we already know that both e.size and e.csize are the same
|
||||
if (e.size != written - locoff) {
|
||||
throw new ZipException(
|
||||
"invalid entry size (expected " + e.size +
|
||||
" but got " + (written - locoff) + " bytes)");
|
||||
}
|
||||
if (e.crc != crc.getValue()) {
|
||||
throw new ZipException(
|
||||
"invalid entry crc-32 (expected 0x" +
|
||||
Long.toHexString(e.crc) + " but got 0x" +
|
||||
Long.toHexString(crc.getValue()) + ")");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("invalid compression method");
|
||||
}
|
||||
crc.reset();
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an array of bytes to the current ZIP entry data. This method
|
||||
* will block until all the bytes are written.
|
||||
* @param b the data to be written
|
||||
* @param off the start offset in the data
|
||||
* @param len the number of bytes that are written
|
||||
* @exception ZipException if a ZIP file error has occurred
|
||||
* @exception IOException if an I/O error has occurred
|
||||
*/
|
||||
public synchronized void write(byte[] b, int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
ensureOpen();
|
||||
if (off < 0 || len < 0 || off > b.length - len) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current == null) {
|
||||
throw new ZipException("no current ZIP entry");
|
||||
}
|
||||
ZipEntry entry = current.entry;
|
||||
switch (entry.method) {
|
||||
case DEFLATED:
|
||||
super.write(b, off, len);
|
||||
break;
|
||||
case STORED:
|
||||
written += len;
|
||||
if (written - locoff > entry.size) {
|
||||
throw new ZipException(
|
||||
"attempt to write past end of STORED entry");
|
||||
}
|
||||
out.write(b, off, len);
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("invalid compression method");
|
||||
}
|
||||
crc.update(b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes writing the contents of the ZIP output stream without closing
|
||||
* the underlying stream. Use this method when applying multiple filters
|
||||
* in succession to the same output stream.
|
||||
* @exception ZipException if a ZIP file error has occurred
|
||||
* @exception IOException if an I/O exception has occurred
|
||||
*/
|
||||
public void finish() throws IOException {
|
||||
ensureOpen();
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
if (current != null) {
|
||||
closeEntry();
|
||||
}
|
||||
// write central directory
|
||||
long off = written;
|
||||
for (XEntry xentry : xentries)
|
||||
writeCEN(xentry);
|
||||
writeEND(off, written - off);
|
||||
finished = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the ZIP output stream as well as the stream being filtered.
|
||||
* @exception ZipException if a ZIP file error has occurred
|
||||
* @exception IOException if an I/O error has occurred
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
if (!closed) {
|
||||
super.close();
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes local file (LOC) header for specified entry.
|
||||
*/
|
||||
private void writeLOC(XEntry xentry) throws IOException {
|
||||
ZipEntry e = xentry.entry;
|
||||
int flag = e.flag;
|
||||
boolean hasZip64 = false;
|
||||
int elen = getExtraLen(e.extra);
|
||||
|
||||
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.xdostime); // last modification time
|
||||
// store size, uncompressed size, and crc-32 in data descriptor
|
||||
// immediately following compressed entry data
|
||||
writeInt(0);
|
||||
writeInt(0);
|
||||
writeInt(0);
|
||||
} else {
|
||||
if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
|
||||
hasZip64 = true;
|
||||
writeShort(45); // ver 4.5 for zip64
|
||||
} else {
|
||||
writeShort(version(e)); // version needed to extract
|
||||
}
|
||||
writeShort(flag); // general purpose bit flag
|
||||
writeShort(e.method); // compression method
|
||||
writeInt(e.xdostime); // last modification time
|
||||
writeInt(e.crc); // crc-32
|
||||
if (hasZip64) {
|
||||
writeInt(ZIP64_MAGICVAL);
|
||||
writeInt(ZIP64_MAGICVAL);
|
||||
elen += 20; //headid(2) + size(2) + size(8) + csize(8)
|
||||
} else {
|
||||
writeInt(e.csize); // compressed size
|
||||
writeInt(e.size); // uncompressed size
|
||||
}
|
||||
}
|
||||
byte[] nameBytes = zc.getBytes(e.name);
|
||||
writeShort(nameBytes.length);
|
||||
|
||||
int elenEXTT = 0; // info-zip extended timestamp
|
||||
int flagEXTT = 0;
|
||||
long umtime = -1;
|
||||
long uatime = -1;
|
||||
long uctime = -1;
|
||||
if (e.mtime != null) {
|
||||
elenEXTT += 4;
|
||||
flagEXTT |= EXTT_FLAG_LMT;
|
||||
umtime = fileTimeToUnixTime(e.mtime);
|
||||
}
|
||||
if (e.atime != null) {
|
||||
elenEXTT += 4;
|
||||
flagEXTT |= EXTT_FLAG_LAT;
|
||||
uatime = fileTimeToUnixTime(e.atime);
|
||||
}
|
||||
if (e.ctime != null) {
|
||||
elenEXTT += 4;
|
||||
flagEXTT |= EXTT_FLAT_CT;
|
||||
uctime = fileTimeToUnixTime(e.ctime);
|
||||
}
|
||||
if (flagEXTT != 0) {
|
||||
// to use ntfs time if any m/a/ctime is beyond unixtime upper bound
|
||||
if (umtime > UPPER_UNIXTIME_BOUND ||
|
||||
uatime > UPPER_UNIXTIME_BOUND ||
|
||||
uctime > UPPER_UNIXTIME_BOUND) {
|
||||
elen += 36; // NTFS time, total 36 bytes
|
||||
} else {
|
||||
elen += (elenEXTT + 5); // headid(2) + size(2) + flag(1) + data
|
||||
}
|
||||
}
|
||||
writeShort(elen);
|
||||
writeBytes(nameBytes, 0, nameBytes.length);
|
||||
if (hasZip64) {
|
||||
writeShort(ZIP64_EXTID);
|
||||
writeShort(16);
|
||||
writeLong(e.size);
|
||||
writeLong(e.csize);
|
||||
}
|
||||
if (flagEXTT != 0) {
|
||||
if (umtime > UPPER_UNIXTIME_BOUND ||
|
||||
uatime > UPPER_UNIXTIME_BOUND ||
|
||||
uctime > UPPER_UNIXTIME_BOUND) {
|
||||
writeShort(EXTID_NTFS); // id
|
||||
writeShort(32); // data size
|
||||
writeInt(0); // reserved
|
||||
writeShort(0x0001); // NTFS attr tag
|
||||
writeShort(24);
|
||||
writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
|
||||
: fileTimeToWinTime(e.mtime));
|
||||
writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
|
||||
: fileTimeToWinTime(e.atime));
|
||||
writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
|
||||
: fileTimeToWinTime(e.ctime));
|
||||
} else {
|
||||
writeShort(EXTID_EXTT);
|
||||
writeShort(elenEXTT + 1); // flag + data
|
||||
writeByte(flagEXTT);
|
||||
if (e.mtime != null)
|
||||
writeInt(umtime);
|
||||
if (e.atime != null)
|
||||
writeInt(uatime);
|
||||
if (e.ctime != null)
|
||||
writeInt(uctime);
|
||||
}
|
||||
}
|
||||
writeExtra(e.extra);
|
||||
locoff = written;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes extra data descriptor (EXT) for specified entry.
|
||||
*/
|
||||
private void writeEXT(ZipEntry e) throws IOException {
|
||||
writeInt(EXTSIG); // EXT header signature
|
||||
writeInt(e.crc); // crc-32
|
||||
if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
|
||||
writeLong(e.csize);
|
||||
writeLong(e.size);
|
||||
} else {
|
||||
writeInt(e.csize); // compressed size
|
||||
writeInt(e.size); // uncompressed size
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write central directory (CEN) header for specified entry.
|
||||
* REMIND: add support for file attributes
|
||||
*/
|
||||
private void writeCEN(XEntry xentry) throws IOException {
|
||||
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 elenZIP64 = 0;
|
||||
boolean hasZip64 = false;
|
||||
|
||||
if (e.csize >= ZIP64_MAGICVAL) {
|
||||
csize = ZIP64_MAGICVAL;
|
||||
elenZIP64 += 8; // csize(8)
|
||||
hasZip64 = true;
|
||||
}
|
||||
if (e.size >= ZIP64_MAGICVAL) {
|
||||
size = ZIP64_MAGICVAL; // size(8)
|
||||
elenZIP64 += 8;
|
||||
hasZip64 = true;
|
||||
}
|
||||
if (xentry.offset >= ZIP64_MAGICVAL) {
|
||||
offset = ZIP64_MAGICVAL;
|
||||
elenZIP64 += 8; // offset(8)
|
||||
hasZip64 = true;
|
||||
}
|
||||
writeInt(CENSIG); // CEN header signature
|
||||
if (hasZip64) {
|
||||
writeShort(45); // ver 4.5 for zip64
|
||||
writeShort(45);
|
||||
} else {
|
||||
writeShort(version); // version made by
|
||||
writeShort(version); // version needed to extract
|
||||
}
|
||||
writeShort(flag); // general purpose bit flag
|
||||
writeShort(e.method); // compression method
|
||||
writeInt(e.xdostime); // 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 = getExtraLen(e.extra);
|
||||
if (hasZip64) {
|
||||
elen += (elenZIP64 + 4);// + headid(2) + datasize(2)
|
||||
}
|
||||
// cen info-zip extended timestamp only outputs mtime
|
||||
// but set the flag for a/ctime, if present in loc
|
||||
int flagEXTT = 0;
|
||||
long umtime = -1;
|
||||
long uatime = -1;
|
||||
long uctime = -1;
|
||||
if (e.mtime != null) {
|
||||
flagEXTT |= EXTT_FLAG_LMT;
|
||||
umtime = fileTimeToUnixTime(e.mtime);
|
||||
}
|
||||
if (e.atime != null) {
|
||||
flagEXTT |= EXTT_FLAG_LAT;
|
||||
uatime = fileTimeToUnixTime(e.atime);
|
||||
}
|
||||
if (e.ctime != null) {
|
||||
flagEXTT |= EXTT_FLAT_CT;
|
||||
uctime = fileTimeToUnixTime(e.ctime);
|
||||
}
|
||||
if (flagEXTT != 0) {
|
||||
// to use ntfs time if any m/a/ctime is beyond unixtime upper bound
|
||||
if (umtime > UPPER_UNIXTIME_BOUND ||
|
||||
uatime > UPPER_UNIXTIME_BOUND ||
|
||||
uctime > UPPER_UNIXTIME_BOUND) {
|
||||
elen += 36; // NTFS time total 36 bytes
|
||||
} else {
|
||||
elen += 9; // headid(2) + sz(2) + flag(1) + mtime (4)
|
||||
}
|
||||
}
|
||||
writeShort(elen);
|
||||
byte[] commentBytes;
|
||||
if (e.comment != null) {
|
||||
commentBytes = zc.getBytes(e.comment);
|
||||
writeShort(Math.min(commentBytes.length, 0xffff));
|
||||
} else {
|
||||
commentBytes = null;
|
||||
writeShort(0);
|
||||
}
|
||||
writeShort(0); // starting disk number
|
||||
writeShort(0); // internal file attributes (unused)
|
||||
writeInt(0); // external file attributes (unused)
|
||||
writeInt(offset); // relative offset of local header
|
||||
writeBytes(nameBytes, 0, nameBytes.length);
|
||||
|
||||
// take care of EXTID_ZIP64 and EXTID_EXTT
|
||||
if (hasZip64) {
|
||||
writeShort(ZIP64_EXTID);// Zip64 extra
|
||||
writeShort(elenZIP64);
|
||||
if (size == ZIP64_MAGICVAL)
|
||||
writeLong(e.size);
|
||||
if (csize == ZIP64_MAGICVAL)
|
||||
writeLong(e.csize);
|
||||
if (offset == ZIP64_MAGICVAL)
|
||||
writeLong(xentry.offset);
|
||||
}
|
||||
if (flagEXTT != 0) {
|
||||
if (umtime > UPPER_UNIXTIME_BOUND ||
|
||||
uatime > UPPER_UNIXTIME_BOUND ||
|
||||
uctime > UPPER_UNIXTIME_BOUND) {
|
||||
writeShort(EXTID_NTFS); // id
|
||||
writeShort(32); // data size
|
||||
writeInt(0); // reserved
|
||||
writeShort(0x0001); // NTFS attr tag
|
||||
writeShort(24);
|
||||
writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
|
||||
: fileTimeToWinTime(e.mtime));
|
||||
writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
|
||||
: fileTimeToWinTime(e.atime));
|
||||
writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
|
||||
: fileTimeToWinTime(e.ctime));
|
||||
} else {
|
||||
writeShort(EXTID_EXTT);
|
||||
if (e.mtime != null) {
|
||||
writeShort(5); // flag + mtime
|
||||
writeByte(flagEXTT);
|
||||
writeInt(umtime);
|
||||
} else {
|
||||
writeShort(1); // flag only
|
||||
writeByte(flagEXTT);
|
||||
}
|
||||
}
|
||||
}
|
||||
writeExtra(e.extra);
|
||||
if (commentBytes != null) {
|
||||
writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes end of central directory (END) header.
|
||||
*/
|
||||
private void writeEND(long off, long len) throws IOException {
|
||||
boolean hasZip64 = false;
|
||||
long xlen = len;
|
||||
long xoff = off;
|
||||
if (xlen >= ZIP64_MAGICVAL) {
|
||||
xlen = ZIP64_MAGICVAL;
|
||||
hasZip64 = true;
|
||||
}
|
||||
if (xoff >= ZIP64_MAGICVAL) {
|
||||
xoff = ZIP64_MAGICVAL;
|
||||
hasZip64 = true;
|
||||
}
|
||||
int count = xentries.size();
|
||||
if (count >= ZIP64_MAGICCOUNT) {
|
||||
hasZip64 |= !inhibitZip64;
|
||||
if (hasZip64) {
|
||||
count = ZIP64_MAGICCOUNT;
|
||||
}
|
||||
}
|
||||
if (hasZip64) {
|
||||
long off64 = written;
|
||||
//zip64 end of central directory record
|
||||
writeInt(ZIP64_ENDSIG); // zip64 END record signature
|
||||
writeLong(ZIP64_ENDHDR - 12); // size of zip64 end
|
||||
writeShort(45); // version made by
|
||||
writeShort(45); // version needed to extract
|
||||
writeInt(0); // number of this disk
|
||||
writeInt(0); // central directory start disk
|
||||
writeLong(xentries.size()); // number of directory entires on disk
|
||||
writeLong(xentries.size()); // number of directory entires
|
||||
writeLong(len); // length of central directory
|
||||
writeLong(off); // offset of central directory
|
||||
|
||||
//zip64 end of central directory locator
|
||||
writeInt(ZIP64_LOCSIG); // zip64 END locator signature
|
||||
writeInt(0); // zip64 END start disk
|
||||
writeLong(off64); // offset of zip64 END
|
||||
writeInt(1); // total number of disks (?)
|
||||
}
|
||||
writeInt(ENDSIG); // END record signature
|
||||
writeShort(0); // number of this disk
|
||||
writeShort(0); // central directory start disk
|
||||
writeShort(count); // number of directory entries on disk
|
||||
writeShort(count); // total number of directory entries
|
||||
writeInt(xlen); // length of central directory
|
||||
writeInt(xoff); // offset of central directory
|
||||
if (comment != null) { // zip file comment
|
||||
writeShort(comment.length);
|
||||
writeBytes(comment, 0, comment.length);
|
||||
} else {
|
||||
writeShort(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the length of extra data without EXTT and ZIP64.
|
||||
*/
|
||||
private int getExtraLen(byte[] extra) {
|
||||
if (extra == null)
|
||||
return 0;
|
||||
int skipped = 0;
|
||||
int len = extra.length;
|
||||
int off = 0;
|
||||
while (off + 4 <= len) {
|
||||
int tag = get16(extra, off);
|
||||
int sz = get16(extra, off + 2);
|
||||
if (sz < 0 || (off + 4 + sz) > len) {
|
||||
break;
|
||||
}
|
||||
if (tag == EXTID_EXTT || tag == EXTID_ZIP64) {
|
||||
skipped += (sz + 4);
|
||||
}
|
||||
off += (sz + 4);
|
||||
}
|
||||
return len - skipped;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes extra data without EXTT and ZIP64.
|
||||
*
|
||||
* Extra timestamp and ZIP64 data is handled/output separately
|
||||
* in writeLOC and writeCEN.
|
||||
*/
|
||||
private void writeExtra(byte[] extra) throws IOException {
|
||||
if (extra != null) {
|
||||
int len = extra.length;
|
||||
int off = 0;
|
||||
while (off + 4 <= len) {
|
||||
int tag = get16(extra, off);
|
||||
int sz = get16(extra, off + 2);
|
||||
if (sz < 0 || (off + 4 + sz) > len) {
|
||||
writeBytes(extra, off, len - off);
|
||||
return;
|
||||
}
|
||||
if (tag != EXTID_EXTT && tag != EXTID_ZIP64) {
|
||||
writeBytes(extra, off, sz + 4);
|
||||
}
|
||||
off += (sz + 4);
|
||||
}
|
||||
if (off < len) {
|
||||
writeBytes(extra, off, len - off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
OutputStream out = this.out;
|
||||
out.write((v >>> 0) & 0xff);
|
||||
out.write((v >>> 8) & 0xff);
|
||||
written += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes a 32-bit int to the output stream in little-endian byte order.
|
||||
*/
|
||||
private void writeInt(long v) throws IOException {
|
||||
OutputStream out = this.out;
|
||||
out.write((int)((v >>> 0) & 0xff));
|
||||
out.write((int)((v >>> 8) & 0xff));
|
||||
out.write((int)((v >>> 16) & 0xff));
|
||||
out.write((int)((v >>> 24) & 0xff));
|
||||
written += 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes a 64-bit int to the output stream in little-endian byte order.
|
||||
*/
|
||||
private void writeLong(long v) throws IOException {
|
||||
OutputStream out = this.out;
|
||||
out.write((int)((v >>> 0) & 0xff));
|
||||
out.write((int)((v >>> 8) & 0xff));
|
||||
out.write((int)((v >>> 16) & 0xff));
|
||||
out.write((int)((v >>> 24) & 0xff));
|
||||
out.write((int)((v >>> 32) & 0xff));
|
||||
out.write((int)((v >>> 40) & 0xff));
|
||||
out.write((int)((v >>> 48) & 0xff));
|
||||
out.write((int)((v >>> 56) & 0xff));
|
||||
written += 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes an array of bytes to the output stream.
|
||||
*/
|
||||
private void writeBytes(byte[] b, int off, int len) throws IOException {
|
||||
super.out.write(b, off, len);
|
||||
written += len;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue