From 9595073c84782d563f44ccba3ed28b59e22ef301 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Sat, 15 Oct 2022 16:07:36 +0530 Subject: [PATCH] Removed unused ZipFs from JDK 8 --- .../com/itsaky/androidide/zipfs/ZipCoder.java | 155 -- .../itsaky/androidide/zipfs/ZipConstants.java | 250 -- .../androidide/zipfs/ZipDirectoryStream.java | 103 - .../zipfs/ZipFileAttributeView.java | 178 -- .../androidide/zipfs/ZipFileAttributes.java | 149 - .../itsaky/androidide/zipfs/ZipFileStore.java | 139 - .../androidide/zipfs/ZipFileSystem.java | 2452 ----------------- .../zipfs/ZipFileSystemProvider.java | 332 --- .../com/itsaky/androidide/zipfs/ZipPath.java | 901 ------ .../com/itsaky/androidide/zipfs/ZipUtils.java | 306 -- .../share/classes/module-info.java | 1 - 11 files changed, 4966 deletions(-) delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipCoder.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipConstants.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipDirectoryStream.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributeView.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributes.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileStore.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystem.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystemProvider.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipPath.java delete mode 100644 src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipUtils.java diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipCoder.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipCoder.java deleted file mode 100644 index cec2b98..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipCoder.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; -import java.nio.charset.CoderResult; -import java.nio.charset.CodingErrorAction; -import java.util.Arrays; - -/** - * Utility class for zipfile name and comment decoding and encoding - * - * @author Xueming Shen - */ - -final class ZipCoder { - - String toString(byte[] ba, int length) { - CharsetDecoder cd = decoder().reset(); - int len = (int)(length * cd.maxCharsPerByte()); - char[] ca = new char[len]; - if (len == 0) - return new String(ca); - ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); - CharBuffer cb = CharBuffer.wrap(ca); - CoderResult cr = cd.decode(bb, cb, true); - if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); - cr = cd.flush(cb); - if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); - return new String(ca, 0, cb.position()); - } - - String toString(byte[] ba) { - return toString(ba, ba.length); - } - - byte[] getBytes(String s) { - CharsetEncoder ce = encoder().reset(); - char[] ca = s.toCharArray(); - int len = (int)(ca.length * ce.maxBytesPerChar()); - byte[] ba = new byte[len]; - if (len == 0) - return ba; - ByteBuffer bb = ByteBuffer.wrap(ba); - CharBuffer cb = CharBuffer.wrap(ca); - CoderResult cr = ce.encode(cb, bb, true); - if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); - cr = ce.flush(bb); - if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); - if (bb.position() == ba.length) // defensive copy? - return ba; - else - return Arrays.copyOf(ba, bb.position()); - } - - // assume invoked only if "this" is not utf8 - byte[] getBytesUTF8(String s) { - if (isutf8) - return getBytes(s); - if (utf8 == null) - utf8 = new ZipCoder(Charset.forName("UTF-8")); - return utf8.getBytes(s); - } - - String toStringUTF8(byte[] ba, int len) { - if (isutf8) - return toString(ba, len); - if (utf8 == null) - utf8 = new ZipCoder(Charset.forName("UTF-8")); - return utf8.toString(ba, len); - } - - boolean isUTF8() { - return isutf8; - } - - private Charset cs; - private boolean isutf8; - private ZipCoder utf8; - - private ZipCoder(Charset cs) { - this.cs = cs; - this.isutf8 = cs.name().equals("UTF-8"); - } - - static ZipCoder get(Charset charset) { - return new ZipCoder(charset); - } - - static ZipCoder get(String csn) { - try { - return new ZipCoder(Charset.forName(csn)); - } catch (Throwable t) { - t.printStackTrace(); - } - return new ZipCoder(Charset.defaultCharset()); - } - - private final ThreadLocal decTL = new ThreadLocal<>(); - private final ThreadLocal encTL = new ThreadLocal<>(); - - private CharsetDecoder decoder() { - CharsetDecoder dec = decTL.get(); - if (dec == null) { - dec = cs.newDecoder() - .onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT); - decTL.set(dec); - } - return dec; - } - - private CharsetEncoder encoder() { - CharsetEncoder enc = encTL.get(); - if (enc == null) { - enc = cs.newEncoder() - .onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT); - encTL.set(enc); - } - return enc; - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipConstants.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipConstants.java deleted file mode 100644 index caf16c3..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipConstants.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - - -/** - * - * @author Xueming Shen - */ - -class ZipConstants { - /* - * Compression methods - */ - static final int METHOD_STORED = 0; - static final int METHOD_DEFLATED = 8; - static final int METHOD_DEFLATED64 = 9; - static final int METHOD_BZIP2 = 12; - static final int METHOD_LZMA = 14; - static final int METHOD_LZ77 = 19; - static final int METHOD_AES = 99; - - /* - * General purpose big flag - */ - static final int FLAG_ENCRYPTED = 0x01; - static final int FLAG_DATADESCR = 0x08; // crc, size and csize in dd - static final int FLAG_EFS = 0x800; // If this bit is set the filename and - // comment fields for this file must be - // encoded using UTF-8. - /* - * Header signatures - */ - static long LOCSIG = 0x04034b50L; // "PK\003\004" - static long EXTSIG = 0x08074b50L; // "PK\007\008" - static long CENSIG = 0x02014b50L; // "PK\001\002" - static long ENDSIG = 0x06054b50L; // "PK\005\006" - - /* - * Header sizes in bytes (including signatures) - */ - static final int LOCHDR = 30; // LOC header size - static final int EXTHDR = 16; // EXT header size - static final int CENHDR = 46; // CEN header size - static final int ENDHDR = 22; // END header size - - /* - * Local file (LOC) header field offsets - */ - static final int LOCVER = 4; // version needed to extract - static final int LOCFLG = 6; // general purpose bit flag - static final int LOCHOW = 8; // compression method - static final int LOCTIM = 10; // modification time - static final int LOCCRC = 14; // uncompressed file crc-32 value - static final int LOCSIZ = 18; // compressed size - static final int LOCLEN = 22; // uncompressed size - static final int LOCNAM = 26; // filename length - static final int LOCEXT = 28; // extra field length - - /* - * Extra local (EXT) header field offsets - */ - static final int EXTCRC = 4; // uncompressed file crc-32 value - static final int EXTSIZ = 8; // compressed size - static final int EXTLEN = 12; // uncompressed size - - /* - * Central directory (CEN) header field offsets - */ - static final int CENVEM = 4; // version made by - static final int CENVER = 6; // version needed to extract - static final int CENFLG = 8; // encrypt, decrypt flags - static final int CENHOW = 10; // compression method - static final int CENTIM = 12; // modification time - static final int CENCRC = 16; // uncompressed file crc-32 value - static final int CENSIZ = 20; // compressed size - static final int CENLEN = 24; // uncompressed size - static final int CENNAM = 28; // filename length - static final int CENEXT = 30; // extra field length - static final int CENCOM = 32; // comment length - static final int CENDSK = 34; // disk number start - static final int CENATT = 36; // internal file attributes - static final int CENATX = 38; // external file attributes - static final int CENOFF = 42; // LOC header offset - - /* - * End of central directory (END) header field offsets - */ - static final int ENDSUB = 8; // number of entries on this disk - static final int ENDTOT = 10; // total number of entries - static final int ENDSIZ = 12; // central directory size in bytes - static final int ENDOFF = 16; // offset of first CEN header - static final int ENDCOM = 20; // zip file comment length - - /* - * ZIP64 constants - */ - static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006" - static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007" - static final int ZIP64_ENDHDR = 56; // ZIP64 end header size - static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size - static final int ZIP64_EXTHDR = 24; // EXT header size - static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID - - static final int ZIP64_MINVAL32 = 0xFFFF; - static final long ZIP64_MINVAL = 0xFFFFFFFFL; - - /* - * Zip64 End of central directory (END) header field offsets - */ - static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir - static final int ZIP64_ENDVEM = 12; // version made by - static final int ZIP64_ENDVER = 14; // version needed to extract - static final int ZIP64_ENDNMD = 16; // number of this disk - static final int ZIP64_ENDDSK = 20; // disk number of start - static final int ZIP64_ENDTOD = 24; // total number of entries on this disk - static final int ZIP64_ENDTOT = 32; // total number of entries - static final int ZIP64_ENDSIZ = 40; // central directory size in bytes - static final int ZIP64_ENDOFF = 48; // offset of first CEN header - static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector - - /* - * Zip64 End of central directory locator field offsets - */ - static final int ZIP64_LOCDSK = 4; // disk number start - static final int ZIP64_LOCOFF = 8; // offset of zip64 end - static final int ZIP64_LOCTOT = 16; // total number of disks - - /* - * Zip64 Extra local (EXT) header field offsets - */ - static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value - static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte - static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte - - /* - * 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_EFS = 0x0017; // Strong Encryption - static final int EXTID_EXTT = 0x5455; // Info-ZIP Extended Timestamp - - /* - * fields access methods - */ - /////////////////////////////////////////////////////// - static final int CH(byte[] b, int n) { - return toUnsignedInt(b[n]); - } - - static final int SH(byte[] b, int n) { - return toUnsignedInt(b[n]) | (toUnsignedInt(b[n + 1]) << 8); - } - - private static int toUnsignedInt(byte x) { - return ((int) x) & 0xff; - } - - static final long LG(byte[] b, int n) { - return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; - } - - static final long LL(byte[] b, int n) { - return (LG(b, n)) | (LG(b, n + 4) << 32); - } - - static final long GETSIG(byte[] b) { - return LG(b, 0); - } - - // local file (LOC) header fields - static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature - static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract - static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags - static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method - static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time - static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data - static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size - static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size - static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length - static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length - - // extra local (EXT) header fields - static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data - static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size - static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size - - // end of central directory header (END) fields - static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk - static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries - static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size - static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset - static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment - static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} - - // zip64 end of central directory recoder fields - static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk - static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries - static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size - static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset - static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset - - // central directory header (CEN) fields - static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } - static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } - static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } - static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } - static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} - static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} - static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} - static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} - static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} - static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} - static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} - static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} - static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} - static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} - static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} - static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} - - /* The END header is followed by a variable length comment of size < 64k. */ - static final long END_MAXLEN = 0xFFFF + ENDHDR; - static final int READBLOCKSZ = 128; -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipDirectoryStream.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipDirectoryStream.java deleted file mode 100644 index e339efb..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipDirectoryStream.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import java.io.IOException; -import java.nio.file.ClosedDirectoryStreamException; -import java.nio.file.DirectoryStream; -import java.nio.file.NotDirectoryException; -import java.nio.file.Path; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * - * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal - */ - -public class ZipDirectoryStream implements DirectoryStream { - - private final ZipFileSystem zipfs; - private final byte[] path; - private final Filter filter; - private volatile boolean isClosed; - private volatile Iterator itr; - - ZipDirectoryStream(ZipPath zipPath, - Filter filter) - throws IOException - { - this.zipfs = zipPath.getFileSystem(); - this.path = zipPath.getResolvedPath(); - this.filter = filter; - // sanity check - if (!zipfs.isDirectory(path)) - throw new NotDirectoryException(zipPath.toString()); - } - - @Override - public synchronized Iterator iterator() { - if (isClosed) - throw new ClosedDirectoryStreamException(); - if (itr != null) - throw new IllegalStateException("Iterator has already been returned"); - - try { - itr = zipfs.iteratorOf(path, filter); - } catch (IOException e) { - throw new IllegalStateException(e); - } - return new Iterator() { - private Path next; - @Override - public boolean hasNext() { - if (isClosed) - return false; - return itr.hasNext(); - } - - @Override - public synchronized Path next() { - if (isClosed) - throw new NoSuchElementException(); - return itr.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - public synchronized void close() throws IOException { - isClosed = true; - } - - -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributeView.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributeView.java deleted file mode 100644 index 87ea037..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributeView.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - - -package com.itsaky.androidide.zipfs; - -import java.io.IOException; -import java.nio.file.attribute.BasicFileAttributeView; -import java.nio.file.attribute.FileAttributeView; -import java.nio.file.attribute.FileTime; -import java.util.LinkedHashMap; -import java.util.Map; - -/* - * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal - */ - -public class ZipFileAttributeView implements BasicFileAttributeView -{ - private static enum AttrID { - size, - creationTime, - lastAccessTime, - lastModifiedTime, - isDirectory, - isRegularFile, - isSymbolicLink, - isOther, - fileKey, - compressedSize, - crc, - method - }; - - private final ZipPath path; - private final boolean isZipView; - - private ZipFileAttributeView(ZipPath path, boolean isZipView) { - this.path = path; - this.isZipView = isZipView; - } - - static V get(ZipPath path, Class type) { - if (type == null) - throw new NullPointerException(); - if (type == BasicFileAttributeView.class) - return (V)new ZipFileAttributeView(path, false); - if (type == ZipFileAttributeView.class) - return (V)new ZipFileAttributeView(path, true); - return null; - } - - static ZipFileAttributeView get(ZipPath path, String type) { - if (type == null) - throw new NullPointerException(); - if (type.equals("basic")) - return new ZipFileAttributeView(path, false); - if (type.equals("zip")) - return new ZipFileAttributeView(path, true); - return null; - } - - @Override - public String name() { - return isZipView ? "zip" : "basic"; - } - - public ZipFileAttributes readAttributes() throws IOException - { - return path.getAttributes(); - } - - @Override - public void setTimes(FileTime lastModifiedTime, - FileTime lastAccessTime, - FileTime createTime) - throws IOException - { - path.setTimes(lastModifiedTime, lastAccessTime, createTime); - } - - void setAttribute(String attribute, Object value) - throws IOException - { - try { - if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime) - setTimes ((FileTime)value, null, null); - if (AttrID.valueOf(attribute) == AttrID.lastAccessTime) - setTimes (null, (FileTime)value, null); - if (AttrID.valueOf(attribute) == AttrID.creationTime) - setTimes (null, null, (FileTime)value); - return; - } catch (IllegalArgumentException x) {} - throw new UnsupportedOperationException("'" + attribute + - "' is unknown or read-only attribute"); - } - - Map readAttributes(String attributes) - throws IOException - { - ZipFileAttributes zfas = readAttributes(); - LinkedHashMap map = new LinkedHashMap<>(); - if ("*".equals(attributes)) { - for (AttrID id : AttrID.values()) { - try { - map.put(id.name(), attribute(id, zfas)); - } catch (IllegalArgumentException x) {} - } - } else { - String[] as = attributes.split(","); - for (String a : as) { - try { - map.put(a, attribute(AttrID.valueOf(a), zfas)); - } catch (IllegalArgumentException x) {} - } - } - return map; - } - - Object attribute(AttrID id, ZipFileAttributes zfas) { - switch (id) { - case size: - return zfas.size(); - case creationTime: - return zfas.creationTime(); - case lastAccessTime: - return zfas.lastAccessTime(); - case lastModifiedTime: - return zfas.lastModifiedTime(); - case isDirectory: - return zfas.isDirectory(); - case isRegularFile: - return zfas.isRegularFile(); - case isSymbolicLink: - return zfas.isSymbolicLink(); - case isOther: - return zfas.isOther(); - case fileKey: - return zfas.fileKey(); - case compressedSize: - if (isZipView) - return zfas.compressedSize(); - break; - case crc: - if (isZipView) - return zfas.crc(); - break; - case method: - if (isZipView) - return zfas.method(); - break; - } - return null; - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributes.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributes.java deleted file mode 100644 index a99f1b0..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileAttributes.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - - -package com.itsaky.androidide.zipfs; - -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; -import java.util.Arrays; -import java.util.Formatter; - -/** - * - * @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal - */ - -public class ZipFileAttributes implements BasicFileAttributes - -{ - private final ZipFileSystem.Entry e; - - ZipFileAttributes(ZipFileSystem.Entry e) { - this.e = e; - } - - ///////// basic attributes /////////// - @Override - public FileTime creationTime() { - if (e.ctime != -1) - return FileTime.fromMillis(e.ctime); - return null; - } - - @Override - public boolean isDirectory() { - return e.isDir(); - } - - @Override - public boolean isOther() { - return false; - } - - @Override - public boolean isRegularFile() { - return !e.isDir(); - } - - @Override - public FileTime lastAccessTime() { - if (e.atime != -1) - return FileTime.fromMillis(e.atime); - return null; - } - - @Override - public FileTime lastModifiedTime() { - return FileTime.fromMillis(e.mtime); - } - - @Override - public long size() { - return e.size; - } - - @Override - public boolean isSymbolicLink() { - return false; - } - - @Override - public Object fileKey() { - return null; - } - - ///////// zip entry attributes /////////// - public long compressedSize() { - return e.csize; - } - - public long crc() { - return e.crc; - } - - public int method() { - return e.method; - } - - public byte[] extra() { - if (e.extra != null) - return Arrays.copyOf(e.extra, e.extra.length); - return null; - } - - public byte[] comment() { - if (e.comment != null) - return Arrays.copyOf(e.comment, e.comment.length); - return null; - } - - public String toString() { - StringBuilder sb = new StringBuilder(1024); - Formatter fm = new Formatter(sb); - if (creationTime() != null) - fm.format(" creationTime : %tc%n", creationTime().toMillis()); - else - fm.format(" creationTime : null%n"); - - if (lastAccessTime() != null) - fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); - else - fm.format(" lastAccessTime : null%n"); - fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); - fm.format(" isRegularFile : %b%n", isRegularFile()); - fm.format(" isDirectory : %b%n", isDirectory()); - fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); - fm.format(" isOther : %b%n", isOther()); - fm.format(" fileKey : %s%n", fileKey()); - fm.format(" size : %d%n", size()); - fm.format(" compressedSize : %d%n", compressedSize()); - fm.format(" crc : %x%n", crc()); - fm.format(" method : %d%n", method()); - fm.close(); - return sb.toString(); - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileStore.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileStore.java deleted file mode 100644 index eda571c..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileStore.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import java.io.IOException; -import java.nio.file.FileStore; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributeView; -import java.nio.file.attribute.FileAttributeView; -import java.nio.file.attribute.FileStoreAttributeView; - -/* - * - * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal - */ - -public class ZipFileStore extends FileStore { - - private final ZipFileSystem zfs; - - ZipFileStore(ZipPath zpath) { - this.zfs = zpath.getFileSystem(); - } - - @Override - public String name() { - return zfs.toString() + "/"; - } - - @Override - public String type() { - return "zipfs"; - } - - @Override - public boolean isReadOnly() { - return zfs.isReadOnly(); - } - - @Override - public boolean supportsFileAttributeView(Class type) { - return (type == BasicFileAttributeView.class || - type == ZipFileAttributeView.class); - } - - @Override - public boolean supportsFileAttributeView(String name) { - return name.equals("basic") || name.equals("zip"); - } - - @Override - @SuppressWarnings("unchecked") - public V getFileStoreAttributeView(Class type) { - if (type == null) - throw new NullPointerException(); - return (V)null; - } - - @Override - public long getTotalSpace() throws IOException { - return new ZipFileStoreAttributes(this).totalSpace(); - } - - @Override - public long getUsableSpace() throws IOException { - return new ZipFileStoreAttributes(this).usableSpace(); - } - - @Override - public long getUnallocatedSpace() throws IOException { - return new ZipFileStoreAttributes(this).unallocatedSpace(); - } - - @Override - public Object getAttribute(String attribute) throws IOException { - if (attribute.equals("totalSpace")) - return getTotalSpace(); - if (attribute.equals("usableSpace")) - return getUsableSpace(); - if (attribute.equals("unallocatedSpace")) - return getUnallocatedSpace(); - throw new UnsupportedOperationException("does not support the given attribute"); - } - - private static class ZipFileStoreAttributes { - final FileStore fstore; - final long size; - - public ZipFileStoreAttributes(ZipFileStore fileStore) - throws IOException - { - Path path = FileSystems.getDefault().getPath(fileStore.name()); - this.size = Files.size(path); - this.fstore = Files.getFileStore(path); - } - - public long totalSpace() { - return size; - } - - public long usableSpace() throws IOException { - if (!fstore.isReadOnly()) - return fstore.getUsableSpace(); - return 0; - } - - public long unallocatedSpace() throws IOException { - if (!fstore.isReadOnly()) - return fstore.getUnallocatedSpace(); - return 0; - } - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystem.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystem.java deleted file mode 100644 index 8676785..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystem.java +++ /dev/null @@ -1,2452 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import static com.itsaky.androidide.zipfs.ZipConstants.CENATT; -import static com.itsaky.androidide.zipfs.ZipConstants.CENATX; -import static com.itsaky.androidide.zipfs.ZipConstants.CENCOM; -import static com.itsaky.androidide.zipfs.ZipConstants.CENCRC; -import static com.itsaky.androidide.zipfs.ZipConstants.CENDSK; -import static com.itsaky.androidide.zipfs.ZipConstants.CENEXT; -import static com.itsaky.androidide.zipfs.ZipConstants.CENFLG; -import static com.itsaky.androidide.zipfs.ZipConstants.CENHDR; -import static com.itsaky.androidide.zipfs.ZipConstants.CENHOW; -import static com.itsaky.androidide.zipfs.ZipConstants.CENLEN; -import static com.itsaky.androidide.zipfs.ZipConstants.CENNAM; -import static com.itsaky.androidide.zipfs.ZipConstants.CENOFF; -import static com.itsaky.androidide.zipfs.ZipConstants.CENSIG; -import static com.itsaky.androidide.zipfs.ZipConstants.CENSIZ; -import static com.itsaky.androidide.zipfs.ZipConstants.CENTIM; -import static com.itsaky.androidide.zipfs.ZipConstants.CENVEM; -import static com.itsaky.androidide.zipfs.ZipConstants.CENVER; -import static com.itsaky.androidide.zipfs.ZipConstants.CH; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDCOM; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDHDR; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDOFF; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDSIG; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDSIZ; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDSUB; -import static com.itsaky.androidide.zipfs.ZipConstants.ENDTOT; -import static com.itsaky.androidide.zipfs.ZipConstants.END_MAXLEN; -import static com.itsaky.androidide.zipfs.ZipConstants.EXTID_EXTT; -import static com.itsaky.androidide.zipfs.ZipConstants.EXTID_NTFS; -import static com.itsaky.androidide.zipfs.ZipConstants.EXTID_ZIP64; -import static com.itsaky.androidide.zipfs.ZipConstants.EXTSIG; -import static com.itsaky.androidide.zipfs.ZipConstants.FLAG_DATADESCR; -import static com.itsaky.androidide.zipfs.ZipConstants.FLAG_EFS; -import static com.itsaky.androidide.zipfs.ZipConstants.LG; -import static com.itsaky.androidide.zipfs.ZipConstants.LL; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCCRC; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCEXT; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCFLG; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCHDR; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCHOW; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCLEN; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCNAM; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCSIG; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCSIZ; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCTIM; -import static com.itsaky.androidide.zipfs.ZipConstants.LOCVER; -import static com.itsaky.androidide.zipfs.ZipConstants.METHOD_DEFLATED; -import static com.itsaky.androidide.zipfs.ZipConstants.METHOD_STORED; -import static com.itsaky.androidide.zipfs.ZipConstants.READBLOCKSZ; -import static com.itsaky.androidide.zipfs.ZipConstants.SH; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_ENDHDR; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_ENDOFF; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_ENDSIG; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_ENDSIZ; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_ENDTOT; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_LOCHDR; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_LOCOFF; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_LOCSIG; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_MINVAL; -import static com.itsaky.androidide.zipfs.ZipConstants.ZIP64_MINVAL32; -import static com.itsaky.androidide.zipfs.ZipUtils.dosToJavaTime; -import static com.itsaky.androidide.zipfs.ZipUtils.javaToDosTime; -import static com.itsaky.androidide.zipfs.ZipUtils.javaToUnixTime; -import static com.itsaky.androidide.zipfs.ZipUtils.javaToWinTime; -import static com.itsaky.androidide.zipfs.ZipUtils.toDirectoryPath; -import static com.itsaky.androidide.zipfs.ZipUtils.toRegexPattern; -import static com.itsaky.androidide.zipfs.ZipUtils.unixToJavaTime; -import static com.itsaky.androidide.zipfs.ZipUtils.winToJavaTime; -import static com.itsaky.androidide.zipfs.ZipUtils.writeBytes; -import static com.itsaky.androidide.zipfs.ZipUtils.writeInt; -import static com.itsaky.androidide.zipfs.ZipUtils.writeLong; -import static com.itsaky.androidide.zipfs.ZipUtils.writeShort; -import static java.lang.Boolean.TRUE; -import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static java.nio.file.StandardOpenOption.APPEND; -import static java.nio.file.StandardOpenOption.CREATE; -import static java.nio.file.StandardOpenOption.CREATE_NEW; -import static java.nio.file.StandardOpenOption.READ; -import static java.nio.file.StandardOpenOption.WRITE; - -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.NonWritableChannelException; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.SeekableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.nio.file.AccessMode; -import java.nio.file.ClosedFileSystemException; -import java.nio.file.CopyOption; -import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.FileStore; -import java.nio.file.FileSystem; -import java.nio.file.FileSystemException; -import java.nio.file.FileSystemNotFoundException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.NotDirectoryException; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.ReadOnlyFileSystemException; -import java.nio.file.StandardOpenOption; -import java.nio.file.WatchService; -import java.nio.file.attribute.FileAttribute; -import java.nio.file.attribute.FileTime; -import java.nio.file.attribute.UserPrincipalLookupService; -import java.nio.file.spi.FileSystemProvider; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.regex.Pattern; -import java.util.zip.CRC32; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.Inflater; -import java.util.zip.InflaterInputStream; -import java.util.zip.ZipError; -import java.util.zip.ZipException; - -/** - * A FileSystem built on a zip file - * - * @author Xueming Shen - */ - -public class ZipFileSystem extends FileSystem { - - private final ZipFileSystemProvider provider; - private final ZipPath defaultdir; - private boolean readOnly = false; - private final Path zfpath; - private final ZipCoder zc; - - // configurable by env map - private final String defaultDir; // default dir for the file system - private final String nameEncoding; // default encoding for name/comment - private final boolean useTempFile; // use a temp file for newOS, default - // is to use BAOS for better performance - private final boolean createNew; // create a new zip if not exists - private static final boolean isWindows = - System.getProperty("os.name").startsWith("Windows"); - - ZipFileSystem(ZipFileSystemProvider provider, - Path zfpath, - Map env) - throws IOException - { - // configurable env setup - this.createNew = "true".equals(env.get("create")); - this.nameEncoding = env.containsKey("encoding") ? - (String)env.get("encoding") : "UTF-8"; - this.useTempFile = TRUE.equals(env.get("useTempFile")); - this.defaultDir = env.containsKey("default.dir") ? - (String)env.get("default.dir") : "/"; - if (this.defaultDir.charAt(0) != '/') - throw new IllegalArgumentException("default dir should be absolute"); - - this.provider = provider; - this.zfpath = zfpath; - if (Files.notExists(zfpath)) { - if (createNew) { - try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) { - new END().write(os, 0); - } - } else { - throw new FileSystemNotFoundException(zfpath.toString()); - } - } - // sm and existence check - zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ); - if (!Files.isWritable(zfpath)) - this.readOnly = true; - this.zc = ZipCoder.get(nameEncoding); - this.defaultdir = new ZipPath(this, getBytes(defaultDir)); - this.ch = Files.newByteChannel(zfpath, READ); - this.cen = initCEN(); - } - - @Override - public FileSystemProvider provider() { - return provider; - } - - @Override - public String getSeparator() { - return "/"; - } - - @Override - public boolean isOpen() { - return isOpen; - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - private void checkWritable() throws IOException { - if (readOnly) - throw new ReadOnlyFileSystemException(); - } - - @Override - public Iterable getRootDirectories() { - ArrayList pathArr = new ArrayList<>(); - pathArr.add(new ZipPath(this, new byte[]{'/'})); - return pathArr; - } - - ZipPath getDefaultDir() { // package private - return defaultdir; - } - - @Override - public ZipPath getPath(String first, String... more) { - String path; - if (more.length == 0) { - path = first; - } else { - StringBuilder sb = new StringBuilder(); - sb.append(first); - for (String segment: more) { - if (segment.length() > 0) { - if (sb.length() > 0) - sb.append('/'); - sb.append(segment); - } - } - path = sb.toString(); - } - return new ZipPath(this, getBytes(path)); - } - - @Override - public UserPrincipalLookupService getUserPrincipalLookupService() { - throw new UnsupportedOperationException(); - } - - @Override - public WatchService newWatchService() { - throw new UnsupportedOperationException(); - } - - FileStore getFileStore(ZipPath path) { - return new ZipFileStore(path); - } - - @Override - public Iterable getFileStores() { - ArrayList list = new ArrayList<>(1); - list.add(new ZipFileStore(new ZipPath(this, new byte[]{'/'}))); - return list; - } - - private static final Set supportedFileAttributeViews = - Collections.unmodifiableSet( - new HashSet(Arrays.asList("basic", "zip"))); - - @Override - public Set supportedFileAttributeViews() { - return supportedFileAttributeViews; - } - - @Override - public String toString() { - return zfpath.toString(); - } - - Path getZipFile() { - return zfpath; - } - - private static final String GLOB_SYNTAX = "glob"; - private static final String REGEX_SYNTAX = "regex"; - - @Override - public PathMatcher getPathMatcher(String syntaxAndInput) { - int pos = syntaxAndInput.indexOf(':'); - if (pos <= 0 || pos == syntaxAndInput.length()) { - throw new IllegalArgumentException(); - } - String syntax = syntaxAndInput.substring(0, pos); - String input = syntaxAndInput.substring(pos + 1); - String expr; - if (syntax.equals(GLOB_SYNTAX)) { - expr = toRegexPattern(input); - } else { - if (syntax.equals(REGEX_SYNTAX)) { - expr = input; - } else { - throw new UnsupportedOperationException("Syntax '" + syntax + - "' not recognized"); - } - } - // return matcher - final Pattern pattern = Pattern.compile(expr); - return new PathMatcher() { - @Override - public boolean matches(Path path) { - return pattern.matcher(path.toString()).matches(); - } - }; - } - - @Override - public void close() throws IOException { - beginWrite(); - try { - if (!isOpen) - return; - isOpen = false; // set closed - } finally { - endWrite(); - } - if (!streams.isEmpty()) { // unlock and close all remaining streams - Set copy = new HashSet<>(streams); - for (InputStream is: copy) - is.close(); - } - beginWrite(); // lock and sync - try { - sync(); - ch.close(); // close the ch just in case no update - } finally { // and sync dose not close the ch - endWrite(); - } - - synchronized (inflaters) { - for (Inflater inf : inflaters) - inf.end(); - } - synchronized (deflaters) { - for (Deflater def : deflaters) - def.end(); - } - - IOException ioe = null; - synchronized (tmppaths) { - for (Path p: tmppaths) { - try { - Files.deleteIfExists(p); - } catch (IOException x) { - if (ioe == null) - ioe = x; - else - ioe.addSuppressed(x); - } - } - } - provider.removeFileSystem(zfpath, this); - if (ioe != null) - throw ioe; - } - - ZipFileAttributes getFileAttributes(byte[] path) - throws IOException - { - Entry e; - beginRead(); - try { - ensureOpen(); - e = getEntry0(path); - if (e == null) { - IndexNode inode = getInode(path); - if (inode == null) - return null; - e = new Entry(inode.name); // pseudo directory - e.method = METHOD_STORED; // STORED for dir - e.mtime = e.atime = e.ctime = -1;// -1 for all times - } - } finally { - endRead(); - } - return new ZipFileAttributes(e); - } - - void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime) - throws IOException - { - checkWritable(); - beginWrite(); - try { - ensureOpen(); - Entry e = getEntry0(path); // ensureOpen checked - if (e == null) - throw new NoSuchFileException(getString(path)); - if (e.type == Entry.CEN) - e.type = Entry.COPY; // copy e - if (mtime != null) - e.mtime = mtime.toMillis(); - if (atime != null) - e.atime = atime.toMillis(); - if (ctime != null) - e.ctime = ctime.toMillis(); - update(e); - } finally { - endWrite(); - } - } - - boolean exists(byte[] path) - throws IOException - { - beginRead(); - try { - ensureOpen(); - return getInode(path) != null; - } finally { - endRead(); - } - } - - boolean isDirectory(byte[] path) - throws IOException - { - beginRead(); - try { - IndexNode n = getInode(path); - return n != null && n.isDir(); - } finally { - endRead(); - } - } - - private ZipPath toZipPath(byte[] path) { - // make it absolute - byte[] p = new byte[path.length + 1]; - p[0] = '/'; - System.arraycopy(path, 0, p, 1, path.length); - return new ZipPath(this, p); - } - - // returns the list of child paths of "path" - Iterator iteratorOf(byte[] path, - DirectoryStream.Filter filter) - throws IOException - { - beginWrite(); // iteration of inodes needs exclusive lock - try { - ensureOpen(); - IndexNode inode = getInode(path); - if (inode == null) - throw new NotDirectoryException(getString(path)); - List list = new ArrayList<>(); - IndexNode child = inode.child; - while (child != null) { - ZipPath zp = toZipPath(child.name); - if (filter == null || filter.accept(zp)) - list.add(zp); - child = child.sibling; - } - return list.iterator(); - } finally { - endWrite(); - } - } - - void createDirectory(byte[] dir, FileAttribute... attrs) - throws IOException - { - checkWritable(); - dir = toDirectoryPath(dir); - beginWrite(); - try { - ensureOpen(); - if (dir.length == 0 || exists(dir)) // root dir, or exiting dir - throw new FileAlreadyExistsException(getString(dir)); - checkParents(dir); - Entry e = new Entry(dir, Entry.NEW); - e.method = METHOD_STORED; // STORED for dir - update(e); - } finally { - endWrite(); - } - } - - void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options) - throws IOException - { - checkWritable(); - if (Arrays.equals(src, dst)) - return; // do nothing, src and dst are the same - - beginWrite(); - try { - ensureOpen(); - Entry eSrc = getEntry0(src); // ensureOpen checked - if (eSrc == null) - throw new NoSuchFileException(getString(src)); - if (eSrc.isDir()) { // spec says to create dst dir - createDirectory(dst); - return; - } - boolean hasReplace = false; - boolean hasCopyAttrs = false; - for (CopyOption opt : options) { - if (opt == REPLACE_EXISTING) - hasReplace = true; - else if (opt == COPY_ATTRIBUTES) - hasCopyAttrs = true; - } - Entry eDst = getEntry0(dst); - if (eDst != null) { - if (!hasReplace) - throw new FileAlreadyExistsException(getString(dst)); - } else { - checkParents(dst); - } - Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry - u.name(dst); // change name - if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) - { - u.type = eSrc.type; // make it the same type - if (!deletesrc) { // if it's not "rename", just take the data - if (eSrc.bytes != null) - u.bytes = Arrays.copyOf(eSrc.bytes, eSrc.bytes.length); - else if (eSrc.file != null) { - u.file = getTempPathForEntry(null); - Files.copy(eSrc.file, u.file, REPLACE_EXISTING); - } - } - } - if (!hasCopyAttrs) - u.mtime = u.atime= u.ctime = System.currentTimeMillis(); - update(u); - if (deletesrc) - updateDelete(eSrc); - } finally { - endWrite(); - } - } - - // Returns an output stream for writing the contents into the specified - // entry. - OutputStream newOutputStream(byte[] path, OpenOption... options) - throws IOException - { - checkWritable(); - boolean hasCreateNew = false; - boolean hasCreate = false; - boolean hasAppend = false; - for (OpenOption opt: options) { - if (opt == READ) - throw new IllegalArgumentException("READ not allowed"); - if (opt == CREATE_NEW) - hasCreateNew = true; - if (opt == CREATE) - hasCreate = true; - if (opt == APPEND) - hasAppend = true; - } - beginRead(); // only need a readlock, the "update()" will - try { // try to obtain a writelock when the os is - ensureOpen(); // being closed. - Entry e = getEntry0(path); - if (e != null) { - if (e.isDir() || hasCreateNew) - throw new FileAlreadyExistsException(getString(path)); - if (hasAppend) { - InputStream is = getInputStream(e); - OutputStream os = getOutputStream(new Entry(e, Entry.NEW)); - copyStream(is, os); - is.close(); - return os; - } - return getOutputStream(new Entry(e, Entry.NEW)); - } else { - if (!hasCreate && !hasCreateNew) - throw new NoSuchFileException(getString(path)); - checkParents(path); - return getOutputStream(new Entry(path, Entry.NEW)); - } - } finally { - endRead(); - } - } - - // Returns an input stream for reading the contents of the specified - // file entry. - InputStream newInputStream(byte[] path) throws IOException { - beginRead(); - try { - ensureOpen(); - Entry e = getEntry0(path); - if (e == null) - throw new NoSuchFileException(getString(path)); - if (e.isDir()) - throw new FileSystemException(getString(path), "is a directory", null); - return getInputStream(e); - } finally { - endRead(); - } - } - - private void checkOptions(Set options) { - // check for options of null type and option is an intance of StandardOpenOption - for (OpenOption option : options) { - if (option == null) - throw new NullPointerException(); - if (!(option instanceof StandardOpenOption)) - throw new IllegalArgumentException(); - } - } - - // Returns a Writable/ReadByteChannel for now. Might consdier to use - // newFileChannel() instead, which dump the entry data into a regular - // file on the default file system and create a FileChannel on top of - // it. - SeekableByteChannel newByteChannel(byte[] path, - Set options, - FileAttribute... attrs) - throws IOException - { - checkOptions(options); - if (options.contains(StandardOpenOption.WRITE) || - options.contains(StandardOpenOption.APPEND)) { - checkWritable(); - beginRead(); - try { - final WritableByteChannel wbc = Channels.newChannel( - newOutputStream(path, options.toArray(new OpenOption[0]))); - long leftover = 0; - if (options.contains(StandardOpenOption.APPEND)) { - Entry e = getEntry0(path); - if (e != null && e.size >= 0) - leftover = e.size; - } - final long offset = leftover; - return new SeekableByteChannel() { - long written = offset; - public boolean isOpen() { - return wbc.isOpen(); - } - - public long position() throws IOException { - return written; - } - - public SeekableByteChannel position(long pos) - throws IOException - { - throw new UnsupportedOperationException(); - } - - public int read(ByteBuffer dst) throws IOException { - throw new UnsupportedOperationException(); - } - - public SeekableByteChannel truncate(long size) - throws IOException - { - throw new UnsupportedOperationException(); - } - - public int write(ByteBuffer src) throws IOException { - int n = wbc.write(src); - written += n; - return n; - } - - public long size() throws IOException { - return written; - } - - public void close() throws IOException { - wbc.close(); - } - }; - } finally { - endRead(); - } - } else { - beginRead(); - try { - ensureOpen(); - Entry e = getEntry0(path); - if (e == null || e.isDir()) - throw new NoSuchFileException(getString(path)); - final ReadableByteChannel rbc = - Channels.newChannel(getInputStream(e)); - final long size = e.size; - return new SeekableByteChannel() { - long read = 0; - public boolean isOpen() { - return rbc.isOpen(); - } - - public long position() throws IOException { - return read; - } - - public SeekableByteChannel position(long pos) - throws IOException - { - throw new UnsupportedOperationException(); - } - - public int read(ByteBuffer dst) throws IOException { - int n = rbc.read(dst); - if (n > 0) { - read += n; - } - return n; - } - - public SeekableByteChannel truncate(long size) - throws IOException - { - throw new NonWritableChannelException(); - } - - public int write (ByteBuffer src) throws IOException { - throw new NonWritableChannelException(); - } - - public long size() throws IOException { - return size; - } - - public void close() throws IOException { - rbc.close(); - } - }; - } finally { - endRead(); - } - } - } - - // Returns a FileChannel of the specified entry. - // - // This implementation creates a temporary file on the default file system, - // copy the entry data into it if the entry exists, and then create a - // FileChannel on top of it. - FileChannel newFileChannel(byte[] path, - Set options, - FileAttribute... attrs) - throws IOException - { - checkOptions(options); - final boolean forWrite = (options.contains(StandardOpenOption.WRITE) || - options.contains(StandardOpenOption.APPEND)); - beginRead(); - try { - ensureOpen(); - Entry e = getEntry0(path); - if (forWrite) { - checkWritable(); - if (e == null) { - if (!options.contains(StandardOpenOption.CREATE_NEW)) - throw new NoSuchFileException(getString(path)); - } else { - if (options.contains(StandardOpenOption.CREATE_NEW)) - throw new FileAlreadyExistsException(getString(path)); - if (e.isDir()) - throw new FileAlreadyExistsException("directory <" - + getString(path) + "> exists"); - } - options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile - } else if (e == null || e.isDir()) { - throw new NoSuchFileException(getString(path)); - } - - final boolean isFCH = (e != null && e.type == Entry.FILECH); - final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path); - final FileChannel fch = tmpfile.getFileSystem() - .provider() - .newFileChannel(tmpfile, options, attrs); - final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH); - if (forWrite) { - u.flag = FLAG_DATADESCR; - u.method = METHOD_DEFLATED; - } - // is there a better way to hook into the FileChannel's close method? - return new FileChannel() { - public int write(ByteBuffer src) throws IOException { - return fch.write(src); - } - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException - { - return fch.write(srcs, offset, length); - } - public long position() throws IOException { - return fch.position(); - } - public FileChannel position(long newPosition) - throws IOException - { - fch.position(newPosition); - return this; - } - public long size() throws IOException { - return fch.size(); - } - public FileChannel truncate(long size) - throws IOException - { - fch.truncate(size); - return this; - } - public void force(boolean metaData) - throws IOException - { - fch.force(metaData); - } - public long transferTo(long position, long count, - WritableByteChannel target) - throws IOException - { - return fch.transferTo(position, count, target); - } - public long transferFrom(ReadableByteChannel src, - long position, long count) - throws IOException - { - return fch.transferFrom(src, position, count); - } - public int read(ByteBuffer dst) throws IOException { - return fch.read(dst); - } - public int read(ByteBuffer dst, long position) - throws IOException - { - return fch.read(dst, position); - } - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException - { - return fch.read(dsts, offset, length); - } - public int write(ByteBuffer src, long position) - throws IOException - { - return fch.write(src, position); - } - public MappedByteBuffer map(MapMode mode, - long position, long size) - throws IOException - { - throw new UnsupportedOperationException(); - } - public FileLock lock(long position, long size, boolean shared) - throws IOException - { - return fch.lock(position, size, shared); - } - public FileLock tryLock(long position, long size, boolean shared) - throws IOException - { - return fch.tryLock(position, size, shared); - } - protected void implCloseChannel() throws IOException { - fch.close(); - if (forWrite) { - u.mtime = System.currentTimeMillis(); - u.size = Files.size(u.file); - - update(u); - } else { - if (!isFCH) // if this is a new fch for reading - removeTempPathForEntry(tmpfile); - } - } - }; - } finally { - endRead(); - } - } - - // the outstanding input streams that need to be closed - private Set streams = - Collections.synchronizedSet(new HashSet()); - - // the ex-channel and ex-path that need to close when their outstanding - // input streams are all closed by the obtainers. - private Set exChClosers = new HashSet<>(); - - private Set tmppaths = Collections.synchronizedSet(new HashSet()); - private Path getTempPathForEntry(byte[] path) throws IOException { - Path tmpPath = createTempFileInSameDirectoryAs(zfpath); - if (path != null) { - Entry e = getEntry0(path); - if (e != null) { - try (InputStream is = newInputStream(path)) { - Files.copy(is, tmpPath, REPLACE_EXISTING); - } - } - } - return tmpPath; - } - - private void removeTempPathForEntry(Path path) throws IOException { - Files.delete(path); - tmppaths.remove(path); - } - - // check if all parents really exit. ZIP spec does not require - // the existence of any "parent directory". - private void checkParents(byte[] path) throws IOException { - beginRead(); - try { - while ((path = getParent(path)) != null && path.length != 0) { - if (!inodes.containsKey(IndexNode.keyOf(path))) { - throw new NoSuchFileException(getString(path)); - } - } - } finally { - endRead(); - } - } - - private static byte[] ROOTPATH = new byte[0]; - private static byte[] getParent(byte[] path) { - int off = path.length - 1; - if (off > 0 && path[off] == '/') // isDirectory - off--; - while (off > 0 && path[off] != '/') { off--; } - if (off <= 0) - return ROOTPATH; - return Arrays.copyOf(path, off + 1); - } - - private final void beginWrite() { - rwlock.writeLock().lock(); - } - - private final void endWrite() { - rwlock.writeLock().unlock(); - } - - private final void beginRead() { - rwlock.readLock().lock(); - } - - private final void endRead() { - rwlock.readLock().unlock(); - } - - /////////////////////////////////////////////////////////////////// - - private volatile boolean isOpen = true; - private final SeekableByteChannel ch; // channel to the zipfile - final byte[] cen; // CEN & ENDHDR - private END end; - private long locpos; // position of first LOC header (usually 0) - - private final ReadWriteLock rwlock = new ReentrantReadWriteLock(); - - // name -> pos (in cen), IndexNode itself can be used as a "key" - private LinkedHashMap inodes; - - final byte[] getBytes(String name) { - return zc.getBytes(name); - } - - final String getString(byte[] name) { - return zc.toString(name); - } - - protected void finalize() throws IOException { - close(); - } - - private long getDataPos(Entry e) throws IOException { - if (e.locoff == -1) { - Entry e2 = getEntry0(e.name); - if (e2 == null) - throw new ZipException("invalid loc for entry <" + e.name + ">"); - e.locoff = e2.locoff; - } - byte[] buf = new byte[LOCHDR]; - if (readFullyAt(buf, 0, buf.length, e.locoff) != buf.length) - throw new ZipException("invalid loc for entry <" + e.name + ">"); - return locpos + e.locoff + LOCHDR + LOCNAM(buf) + LOCEXT(buf); - } - - // Reads len bytes of data from the specified offset into buf. - // Returns the total number of bytes read. - // Each/every byte read from here (except the cen, which is mapped). - final long readFullyAt(byte[] buf, int off, long len, long pos) - throws IOException - { - ByteBuffer bb = ByteBuffer.wrap(buf); - bb.position(off); - bb.limit((int)(off + len)); - return readFullyAt(bb, pos); - } - - private final long readFullyAt(ByteBuffer bb, long pos) - throws IOException - { - synchronized(ch) { - return ch.position(pos).read(bb); - } - } - - // Searches for end of central directory (END) header. The contents of - // the END header will be read and placed in endbuf. Returns the file - // position of the END header, otherwise returns -1 if the END header - // was not found or an error occurred. - private END findEND() throws IOException - { - byte[] buf = new byte[READBLOCKSZ]; - long ziplen = ch.size(); - long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; - long minPos = minHDR - (buf.length - ENDHDR); - - for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) - { - int off = 0; - if (pos < 0) { - // Pretend there are some NUL bytes before start of file - off = (int)-pos; - Arrays.fill(buf, 0, off, (byte)0); - } - int len = buf.length - off; - if (readFullyAt(buf, off, len, pos + off) != len) - zerror("zip END header not found"); - - // Now scan the block backwards for END header signature - for (int i = buf.length - ENDHDR; i >= 0; i--) { - if (buf[i+0] == (byte)'P' && - buf[i+1] == (byte)'K' && - buf[i+2] == (byte)'\005' && - buf[i+3] == (byte)'\006' && - (pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) { - // Found END header - buf = Arrays.copyOfRange(buf, i, i + ENDHDR); - END end = new END(); - end.endsub = ENDSUB(buf); - end.centot = ENDTOT(buf); - end.cenlen = ENDSIZ(buf); - end.cenoff = ENDOFF(buf); - end.comlen = ENDCOM(buf); - end.endpos = pos + i; - if (end.cenlen == ZIP64_MINVAL || - end.cenoff == ZIP64_MINVAL || - end.centot == ZIP64_MINVAL32) - { - // need to find the zip64 end; - byte[] loc64 = new byte[ZIP64_LOCHDR]; - if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) - != loc64.length) { - return end; - } - long end64pos = ZIP64_LOCOFF(loc64); - byte[] end64buf = new byte[ZIP64_ENDHDR]; - if (readFullyAt(end64buf, 0, end64buf.length, end64pos) - != end64buf.length) { - return end; - } - // end64 found, re-calcualte everything. - end.cenlen = ZIP64_ENDSIZ(end64buf); - end.cenoff = ZIP64_ENDOFF(end64buf); - end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g - end.endpos = end64pos; - } - return end; - } - } - } - zerror("zip END header not found"); - return null; //make compiler happy - } - - // Reads zip file central directory. Returns the file position of first - // CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL - // then the error was a zip format error and zip->msg has the error text. - // Always pass in -1 for knownTotal; it's used for a recursive call. - private byte[] initCEN() throws IOException { - end = findEND(); - if (end.endpos == 0) { - inodes = new LinkedHashMap<>(10); - locpos = 0; - buildNodeTree(); - return null; // only END header present - } - if (end.cenlen > end.endpos) - zerror("invalid END header (bad central directory size)"); - long cenpos = end.endpos - end.cenlen; // position of CEN table - - // Get position of first local file (LOC) header, taking into - // account that there may be a stub prefixed to the zip file. - locpos = cenpos - end.cenoff; - if (locpos < 0) - zerror("invalid END header (bad central directory offset)"); - - // read in the CEN and END - byte[] cen = new byte[(int)(end.cenlen + ENDHDR)]; - if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { - zerror("read CEN tables failed"); - } - // Iterate through the entries in the central directory - inodes = new LinkedHashMap<>(end.centot + 1); - int pos = 0; - int limit = cen.length - ENDHDR; - while (pos < limit) { - if (CENSIG(cen, pos) != CENSIG) - zerror("invalid CEN header (bad signature)"); - int method = CENHOW(cen, pos); - int nlen = CENNAM(cen, pos); - int elen = CENEXT(cen, pos); - int clen = CENCOM(cen, pos); - if ((CENFLG(cen, pos) & 1) != 0) - zerror("invalid CEN header (encrypted entry)"); - if (method != METHOD_STORED && method != METHOD_DEFLATED) - zerror("invalid CEN header (unsupported compression method: " + method + ")"); - if (pos + CENHDR + nlen > limit) - zerror("invalid CEN header (bad header size)"); - byte[] name = Arrays.copyOfRange(cen, pos + CENHDR, pos + CENHDR + nlen); - IndexNode inode = new IndexNode(name, pos); - inodes.put(inode, inode); - // skip ext and comment - pos += (CENHDR + nlen + elen + clen); - } - if (pos + ENDHDR != cen.length) { - zerror("invalid CEN header (bad header size)"); - } - buildNodeTree(); - return cen; - } - - private void ensureOpen() throws IOException { - if (!isOpen) - throw new ClosedFileSystemException(); - } - - // Creates a new empty temporary file in the same directory as the - // specified file. A variant of Files.createTempFile. - private Path createTempFileInSameDirectoryAs(Path path) - throws IOException - { - Path parent = path.toAbsolutePath().getParent(); - Path dir = (parent == null)? path.getFileSystem().getPath(".") : parent; - Path tmpPath = Files.createTempFile(dir, "zipfstmp", null); - tmppaths.add(tmpPath); - return tmpPath; - } - - ////////////////////update & sync ////////////////////////////////////// - - private boolean hasUpdate = false; - - // shared key. consumer guarantees the "writeLock" before use it. - private final IndexNode LOOKUPKEY = IndexNode.keyOf(null); - - private void updateDelete(IndexNode inode) { - beginWrite(); - try { - removeFromTree(inode); - inodes.remove(inode); - hasUpdate = true; - } finally { - endWrite(); - } - } - - private void update(Entry e) { - beginWrite(); - try { - IndexNode old = inodes.put(e, e); - if (old != null) { - removeFromTree(old); - } - if (e.type == Entry.NEW || e.type == Entry.FILECH) { - IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(e.name))); - e.sibling = parent.child; - parent.child = e; - } - hasUpdate = true; - } finally { - endWrite(); - } - } - - // copy over the whole LOC entry (header if necessary, data and ext) from - // old zip to the new one. - private long copyLOCEntry(Entry e, boolean updateHeader, - OutputStream os, - long written, byte[] buf) - throws IOException - { - long locoff = e.locoff; // where to read - e.locoff = written; // update the e.locoff with new value - - // calculate the size need to write out - long size = 0; - // if there is A ext - if ((e.flag & FLAG_DATADESCR) != 0) { - if (e.size >= ZIP64_MINVAL || e.csize >= ZIP64_MINVAL) - size = 24; - else - size = 16; - } - // read loc, use the original loc.elen/nlen - if (readFullyAt(buf, 0, LOCHDR , locoff) != LOCHDR) - throw new ZipException("loc: reading failed"); - if (updateHeader) { - locoff += LOCHDR + LOCNAM(buf) + LOCEXT(buf); // skip header - size += e.csize; - written = e.writeLOC(os) + size; - } else { - os.write(buf, 0, LOCHDR); // write out the loc header - locoff += LOCHDR; - // use e.csize, LOCSIZ(buf) is zero if FLAG_DATADESCR is on - // size += LOCNAM(buf) + LOCEXT(buf) + LOCSIZ(buf); - size += LOCNAM(buf) + LOCEXT(buf) + e.csize; - written = LOCHDR + size; - } - int n; - while (size > 0 && - (n = (int)readFullyAt(buf, 0, buf.length, locoff)) != -1) - { - if (size < n) - n = (int)size; - os.write(buf, 0, n); - size -= n; - locoff += n; - } - return written; - } - - // sync the zip file system, if there is any udpate - private void sync() throws IOException { - //System.out.printf("->sync(%s) starting....!%n", toString()); - // check ex-closer - if (!exChClosers.isEmpty()) { - for (ExChannelCloser ecc : exChClosers) { - if (ecc.streams.isEmpty()) { - ecc.ch.close(); - Files.delete(ecc.path); - exChClosers.remove(ecc); - } - } - } - if (!hasUpdate) - return; - Path tmpFile = createTempFileInSameDirectoryAs(zfpath); - try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(tmpFile, WRITE))) - { - ArrayList elist = new ArrayList<>(inodes.size()); - long written = 0; - byte[] buf = new byte[8192]; - Entry e = null; - - // write loc - for (IndexNode inode : inodes.values()) { - if (inode instanceof Entry) { // an updated inode - e = (Entry)inode; - try { - if (e.type == Entry.COPY) { - // entry copy: the only thing changed is the "name" - // and "nlen" in LOC header, so we udpate/rewrite the - // LOC in new file and simply copy the rest (data and - // ext) without enflating/deflating from the old zip - // file LOC entry. - written += copyLOCEntry(e, true, os, written, buf); - } else { // NEW, FILECH or CEN - e.locoff = written; - written += e.writeLOC(os); // write loc header - if (e.bytes != null) { // in-memory, deflated - os.write(e.bytes); // already - written += e.bytes.length; - } else if (e.file != null) { // tmp file - try (InputStream is = Files.newInputStream(e.file)) { - int n; - if (e.type == Entry.NEW) { // deflated already - while ((n = is.read(buf)) != -1) { - os.write(buf, 0, n); - written += n; - } - } else if (e.type == Entry.FILECH) { - // the data are not deflated, use ZEOS - try (OutputStream os2 = new EntryOutputStream(e, os)) { - while ((n = is.read(buf)) != -1) { - os2.write(buf, 0, n); - } - } - written += e.csize; - if ((e.flag & FLAG_DATADESCR) != 0) - written += e.writeEXT(os); - } - } - Files.delete(e.file); - tmppaths.remove(e.file); - } else { - // dir, 0-length data - } - } - elist.add(e); - } catch (IOException x) { - x.printStackTrace(); // skip any in-accurate entry - } - } else { // unchanged inode - if (inode.pos == -1) { - continue; // pseudo directory node - } - e = Entry.readCEN(this, inode.pos); - try { - written += copyLOCEntry(e, false, os, written, buf); - elist.add(e); - } catch (IOException x) { - x.printStackTrace(); // skip any wrong entry - } - } - } - - // now write back the cen and end table - end.cenoff = written; - for (Entry entry : elist) { - written += entry.writeCEN(os); - } - end.centot = elist.size(); - end.cenlen = written - end.cenoff; - end.write(os, written); - } - if (!streams.isEmpty()) { - // - // TBD: ExChannelCloser should not be necessary if we only - // sync when being closed, all streams should have been - // closed already. Keep the logic here for now. - // - // There are outstanding input streams open on existing "ch", - // so, don't close the "cha" and delete the "file for now, let - // the "ex-channel-closer" to handle them - ExChannelCloser ecc = new ExChannelCloser( - createTempFileInSameDirectoryAs(zfpath), - ch, - streams); - Files.move(zfpath, ecc.path, REPLACE_EXISTING); - exChClosers.add(ecc); - streams = Collections.synchronizedSet(new HashSet()); - } else { - ch.close(); - Files.delete(zfpath); - } - - Files.move(tmpFile, zfpath, REPLACE_EXISTING); - hasUpdate = false; // clear - /* - if (isOpen) { - ch = zfpath.newByteChannel(READ); // re-fresh "ch" and "cen" - cen = initCEN(); - } - */ - //System.out.printf("->sync(%s) done!%n", toString()); - } - - private IndexNode getInode(byte[] path) { - if (path == null) - throw new NullPointerException("path"); - IndexNode key = IndexNode.keyOf(path); - IndexNode inode = inodes.get(key); - if (inode == null && - (path.length == 0 || path[path.length -1] != '/')) { - // if does not ends with a slash - path = Arrays.copyOf(path, path.length + 1); - path[path.length - 1] = '/'; - inode = inodes.get(key.as(path)); - } - return inode; - } - - private Entry getEntry0(byte[] path) throws IOException { - IndexNode inode = getInode(path); - if (inode instanceof Entry) - return (Entry)inode; - if (inode == null || inode.pos == -1) - return null; - return Entry.readCEN(this, inode.pos); - } - - public void deleteFile(byte[] path, boolean failIfNotExists) - throws IOException - { - checkWritable(); - - IndexNode inode = getInode(path); - if (inode == null) { - if (path != null && path.length == 0) - throw new ZipException("root directory can't not be delete"); - if (failIfNotExists) - throw new NoSuchFileException(getString(path)); - } else { - if (inode.isDir() && inode.child != null) - throw new DirectoryNotEmptyException(getString(path)); - updateDelete(inode); - } - } - - private static void copyStream(InputStream is, OutputStream os) - throws IOException - { - byte[] copyBuf = new byte[8192]; - int n; - while ((n = is.read(copyBuf)) != -1) { - os.write(copyBuf, 0, n); - } - } - - // Returns an out stream for either - // (1) writing the contents of a new entry, if the entry exits, or - // (2) updating/replacing the contents of the specified existing entry. - private OutputStream getOutputStream(Entry e) throws IOException { - - if (e.mtime == -1) - e.mtime = System.currentTimeMillis(); - if (e.method == -1) - e.method = METHOD_DEFLATED; // TBD: use default method - // store size, compressed size, and crc-32 in LOC header - e.flag = 0; - if (zc.isUTF8()) - e.flag |= FLAG_EFS; - OutputStream os; - if (useTempFile) { - e.file = getTempPathForEntry(null); - os = Files.newOutputStream(e.file, WRITE); - } else { - os = new ByteArrayOutputStream((e.size > 0)? (int)e.size : 8192); - } - return new EntryOutputStream(e, os); - } - - private InputStream getInputStream(Entry e) - throws IOException - { - InputStream eis = null; - - if (e.type == Entry.NEW) { - if (e.bytes != null) - eis = new ByteArrayInputStream(e.bytes); - else if (e.file != null) - eis = Files.newInputStream(e.file); - else - throw new ZipException("update entry data is missing"); - } else if (e.type == Entry.FILECH) { - // FILECH result is un-compressed. - eis = Files.newInputStream(e.file); - // TBD: wrap to hook close() - // streams.add(eis); - return eis; - } else { // untouced CEN or COPY - eis = new EntryInputStream(e, ch); - } - if (e.method == METHOD_DEFLATED) { - // MORE: Compute good size for inflater stream: - long bufSize = e.size + 2; // Inflater likes a bit of slack - if (bufSize > 65536) - bufSize = 8192; - final long size = e.size; - eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) { - - private boolean isClosed = false; - public void close() throws IOException { - if (!isClosed) { - releaseInflater(inf); - this.in.close(); - isClosed = true; - streams.remove(this); - } - } - // Override fill() method to provide an extra "dummy" byte - // at the end of the input stream. This is required when - // using the "nowrap" Inflater option. (it appears the new - // zlib in 7 does not need it, but keep it for now) - protected void fill() throws IOException { - if (eof) { - throw new EOFException( - "Unexpected end of ZLIB input stream"); - } - len = this.in.read(buf, 0, buf.length); - if (len == -1) { - buf[0] = 0; - len = 1; - eof = true; - } - inf.setInput(buf, 0, len); - } - private boolean eof; - - public int available() throws IOException { - if (isClosed) - return 0; - long avail = size - inf.getBytesWritten(); - return avail > (long) Integer.MAX_VALUE ? - Integer.MAX_VALUE : (int) avail; - } - }; - } else if (e.method == METHOD_STORED) { - // TBD: wrap/ it does not seem necessary - } else { - throw new ZipException("invalid compression method"); - } - streams.add(eis); - return eis; - } - - // Inner class implementing the input stream used to read - // a (possibly compressed) zip file entry. - private class EntryInputStream extends InputStream { - private final SeekableByteChannel zfch; // local ref to zipfs's "ch". zipfs.ch might - // point to a new channel after sync() - private long pos; // current position within entry data - protected long rem; // number of remaining bytes within entry - protected final long size; // uncompressed size of this entry - - EntryInputStream(Entry e, SeekableByteChannel zfch) - throws IOException - { - this.zfch = zfch; - rem = e.csize; - size = e.size; - pos = getDataPos(e); - } - public int read(byte b[], int off, int len) throws IOException { - ensureOpen(); - if (rem == 0) { - return -1; - } - if (len <= 0) { - return 0; - } - if (len > rem) { - len = (int) rem; - } - // readFullyAt() - long n = 0; - ByteBuffer bb = ByteBuffer.wrap(b); - bb.position(off); - bb.limit(off + len); - synchronized(zfch) { - n = zfch.position(pos).read(bb); - } - if (n > 0) { - pos += n; - rem -= n; - } - if (rem == 0) { - close(); - } - return (int)n; - } - public int read() throws IOException { - byte[] b = new byte[1]; - if (read(b, 0, 1) == 1) { - return b[0] & 0xff; - } else { - return -1; - } - } - public long skip(long n) throws IOException { - ensureOpen(); - if (n > rem) - n = rem; - pos += n; - rem -= n; - if (rem == 0) { - close(); - } - return n; - } - public int available() { - return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; - } - public long size() { - return size; - } - public void close() { - rem = 0; - streams.remove(this); - } - } - - class EntryOutputStream extends DeflaterOutputStream - { - private CRC32 crc; - private Entry e; - private long written; - - EntryOutputStream(Entry e, OutputStream os) - throws IOException - { - super(os, getDeflater()); - if (e == null) - throw new NullPointerException("Zip entry is null"); - this.e = e; - crc = new CRC32(); - } - - @Override - public void write(byte b[], int off, int len) throws IOException { - if (e.type != Entry.FILECH) // only from sync - ensureOpen(); - if (off < 0 || len < 0 || off > b.length - len) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return; - } - switch (e.method) { - case METHOD_DEFLATED: - super.write(b, off, len); - break; - case METHOD_STORED: - written += len; - out.write(b, off, len); - break; - default: - throw new ZipException("invalid compression method"); - } - crc.update(b, off, len); - } - - @Override - public void close() throws IOException { - // TBD ensureOpen(); - switch (e.method) { - case METHOD_DEFLATED: - finish(); - e.size = def.getBytesRead(); - e.csize = def.getBytesWritten(); - e.crc = crc.getValue(); - break; - case METHOD_STORED: - // we already know that both e.size and e.csize are the same - e.size = e.csize = written; - e.crc = crc.getValue(); - break; - default: - throw new ZipException("invalid compression method"); - } - //crc.reset(); - if (out instanceof ByteArrayOutputStream) - e.bytes = ((ByteArrayOutputStream)out).toByteArray(); - - if (e.type == Entry.FILECH) { - releaseDeflater(def); - return; - } - super.close(); - releaseDeflater(def); - update(e); - } - } - - static void zerror(String msg) { - throw new ZipError(msg); - } - - // Maxmum number of de/inflater we cache - private final int MAX_FLATER = 20; - // List of available Inflater objects for decompression - private final List inflaters = new ArrayList<>(); - - // Gets an inflater from the list of available inflaters or allocates - // a new one. - private Inflater getInflater() { - synchronized (inflaters) { - int size = inflaters.size(); - if (size > 0) { - Inflater inf = inflaters.remove(size - 1); - return inf; - } else { - return new Inflater(true); - } - } - } - - // Releases the specified inflater to the list of available inflaters. - private void releaseInflater(Inflater inf) { - synchronized (inflaters) { - if (inflaters.size() < MAX_FLATER) { - inf.reset(); - inflaters.add(inf); - } else { - inf.end(); - } - } - } - - // List of available Deflater objects for compression - private final List deflaters = new ArrayList<>(); - - // Gets an deflater from the list of available deflaters or allocates - // a new one. - private Deflater getDeflater() { - synchronized (deflaters) { - int size = deflaters.size(); - if (size > 0) { - Deflater def = deflaters.remove(size - 1); - return def; - } else { - return new Deflater(Deflater.DEFAULT_COMPRESSION, true); - } - } - } - - // Releases the specified inflater to the list of available inflaters. - private void releaseDeflater(Deflater def) { - synchronized (deflaters) { - if (inflaters.size() < MAX_FLATER) { - def.reset(); - deflaters.add(def); - } else { - def.end(); - } - } - } - - // End of central directory record - static class END { - int disknum; - int sdisknum; - int endsub; // endsub - int centot; // 4 bytes - long cenlen; // 4 bytes - long cenoff; // 4 bytes - int comlen; // comment length - byte[] comment; - - /* members of Zip64 end of central directory locator */ - int diskNum; - long endpos; - int disktot; - - void write(OutputStream os, long offset) throws IOException { - boolean hasZip64 = false; - long xlen = cenlen; - long xoff = cenoff; - if (xlen >= ZIP64_MINVAL) { - xlen = ZIP64_MINVAL; - hasZip64 = true; - } - if (xoff >= ZIP64_MINVAL) { - xoff = ZIP64_MINVAL; - hasZip64 = true; - } - int count = centot; - if (count >= ZIP64_MINVAL32) { - count = ZIP64_MINVAL32; - hasZip64 = true; - } - if (hasZip64) { - long off64 = offset; - //zip64 end of central directory record - writeInt(os, ZIP64_ENDSIG); // zip64 END record signature - writeLong(os, ZIP64_ENDHDR - 12); // size of zip64 end - writeShort(os, 45); // version made by - writeShort(os, 45); // version needed to extract - writeInt(os, 0); // number of this disk - writeInt(os, 0); // central directory start disk - writeLong(os, centot); // number of directory entires on disk - writeLong(os, centot); // number of directory entires - writeLong(os, cenlen); // length of central directory - writeLong(os, cenoff); // offset of central directory - - //zip64 end of central directory locator - writeInt(os, ZIP64_LOCSIG); // zip64 END locator signature - writeInt(os, 0); // zip64 END start disk - writeLong(os, off64); // offset of zip64 END - writeInt(os, 1); // total number of disks (?) - } - writeInt(os, ENDSIG); // END record signature - writeShort(os, 0); // number of this disk - writeShort(os, 0); // central directory start disk - writeShort(os, count); // number of directory entries on disk - writeShort(os, count); // total number of directory entries - writeInt(os, xlen); // length of central directory - writeInt(os, xoff); // offset of central directory - if (comment != null) { // zip file comment - writeShort(os, comment.length); - writeBytes(os, comment); - } else { - writeShort(os, 0); - } - } - } - - // Internal node that links a "name" to its pos in cen table. - // The node itself can be used as a "key" to lookup itself in - // the HashMap inodes. - static class IndexNode { - byte[] name; - int hashcode; // node is hashable/hashed by its name - int pos = -1; // postion in cen table, -1 menas the - // entry does not exists in zip file - IndexNode(byte[] name, int pos) { - name(name); - this.pos = pos; - } - - final static IndexNode keyOf(byte[] name) { // get a lookup key; - return new IndexNode(name, -1); - } - - final void name(byte[] name) { - this.name = name; - this.hashcode = Arrays.hashCode(name); - } - - final IndexNode as(byte[] name) { // reuse the node, mostly - name(name); // as a lookup "key" - return this; - } - - boolean isDir() { - return name != null && - (name.length == 0 || name[name.length - 1] == '/'); - } - - public boolean equals(Object other) { - if (!(other instanceof IndexNode)) { - return false; - } - return Arrays.equals(name, ((IndexNode)other).name); - } - - public int hashCode() { - return hashcode; - } - - IndexNode() {} - IndexNode sibling; - IndexNode child; // 1st child - } - - static class Entry extends IndexNode { - - static final int CEN = 1; // entry read from cen - static final int NEW = 2; // updated contents in bytes or file - static final int FILECH = 3; // fch update in "file" - static final int COPY = 4; // copy of a CEN entry - - - byte[] bytes; // updated content bytes - Path file; // use tmp file to store bytes; - int type = CEN; // default is the entry read from cen - - // entry attributes - int version; - int flag; - int method = -1; // compression method - long mtime = -1; // last modification time (in DOS time) - long atime = -1; // last access time - long ctime = -1; // create time - long crc = -1; // crc-32 of entry data - long csize = -1; // compressed size of entry data - long size = -1; // uncompressed size of entry data - byte[] extra; - - // cen - int versionMade; - int disk; - int attrs; - long attrsEx; - long locoff; - byte[] comment; - - Entry() {} - - Entry(byte[] name) { - name(name); - this.mtime = System.currentTimeMillis(); - this.crc = 0; - this.size = 0; - this.csize = 0; - this.method = METHOD_DEFLATED; - } - - Entry(byte[] name, int type) { - this(name); - this.type = type; - } - - Entry (Entry e, int type) { - name(e.name); - this.version = e.version; - this.ctime = e.ctime; - this.atime = e.atime; - this.mtime = e.mtime; - this.crc = e.crc; - this.size = e.size; - this.csize = e.csize; - this.method = e.method; - this.extra = e.extra; - this.versionMade = e.versionMade; - this.disk = e.disk; - this.attrs = e.attrs; - this.attrsEx = e.attrsEx; - this.locoff = e.locoff; - this.comment = e.comment; - this.type = type; - } - - Entry (byte[] name, Path file, int type) { - this(name, type); - this.file = file; - this.method = METHOD_STORED; - } - - int version() throws ZipException { - if (method == METHOD_DEFLATED) - return 20; - else if (method == METHOD_STORED) - return 10; - throw new ZipException("unsupported compression method"); - } - - ///////////////////// CEN ////////////////////// - static Entry readCEN(ZipFileSystem zipfs, int pos) - throws IOException - { - return new Entry().cen(zipfs, pos); - } - - private Entry cen(ZipFileSystem zipfs, int pos) - throws IOException - { - byte[] cen = zipfs.cen; - if (CENSIG(cen, pos) != CENSIG) - zerror("invalid CEN header (bad signature)"); - versionMade = CENVEM(cen, pos); - version = CENVER(cen, pos); - flag = CENFLG(cen, pos); - method = CENHOW(cen, pos); - mtime = dosToJavaTime(CENTIM(cen, pos)); - crc = CENCRC(cen, pos); - csize = CENSIZ(cen, pos); - size = CENLEN(cen, pos); - int nlen = CENNAM(cen, pos); - int elen = CENEXT(cen, pos); - int clen = CENCOM(cen, pos); - disk = CENDSK(cen, pos); - attrs = CENATT(cen, pos); - attrsEx = CENATX(cen, pos); - locoff = CENOFF(cen, pos); - - pos += CENHDR; - name(Arrays.copyOfRange(cen, pos, pos + nlen)); - - pos += nlen; - if (elen > 0) { - extra = Arrays.copyOfRange(cen, pos, pos + elen); - pos += elen; - readExtra(zipfs); - } - if (clen > 0) { - comment = Arrays.copyOfRange(cen, pos, pos + clen); - } - return this; - } - - int writeCEN(OutputStream os) throws IOException - { - 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 - - // confirm size/length - int nlen = (name != null) ? name.length : 0; - int elen = (extra != null) ? extra.length : 0; - int clen = (comment != null) ? comment.length : 0; - if (csize >= ZIP64_MINVAL) { - csize0 = ZIP64_MINVAL; - elen64 += 8; // csize(8) - } - if (size >= ZIP64_MINVAL) { - size0 = ZIP64_MINVAL; // size(8) - elen64 += 8; - } - if (locoff >= ZIP64_MINVAL) { - locoff0 = ZIP64_MINVAL; - elen64 += 8; // offset(8) - } - if (elen64 != 0) - elen64 += 4; // header and data sz 4 bytes - - if (atime != -1) { - if (isWindows) // use NTFS - elenNTFS = 36; // total 36 bytes - else // Extended Timestamp otherwise - elenEXTT = 9; // only mtime in cen - } - writeInt(os, CENSIG); // CEN header signature - if (elen64 != 0) { - writeShort(os, 45); // ver 4.5 for zip64 - writeShort(os, 45); - } else { - writeShort(os, version0); // version made by - writeShort(os, version0); // version needed to extract - } - writeShort(os, flag); // general purpose bit flag - writeShort(os, method); // compression method - // last modification time - writeInt(os, (int)javaToDosTime(mtime)); - writeInt(os, crc); // crc-32 - writeInt(os, csize0); // compressed size - writeInt(os, size0); // uncompressed size - writeShort(os, name.length); - writeShort(os, elen + elen64 + elenNTFS + elenEXTT); - - if (comment != null) { - writeShort(os, Math.min(clen, 0xffff)); - } else { - writeShort(os, 0); - } - writeShort(os, 0); // starting disk number - writeShort(os, 0); // internal file attributes (unused) - writeInt(os, 0); // external file attributes (unused) - writeInt(os, locoff0); // relative offset of local header - writeBytes(os, name); - if (elen64 != 0) { - writeShort(os, EXTID_ZIP64);// Zip64 extra - writeShort(os, elen64 - 4); // size of "this" extra block - if (size0 == ZIP64_MINVAL) - writeLong(os, size); - if (csize0 == ZIP64_MINVAL) - writeLong(os, csize); - if (locoff0 == ZIP64_MINVAL) - writeLong(os, locoff); - } - if (elenNTFS != 0) { - // System.out.println("writing NTFS:" + elenNTFS); - 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); - if (ctime == -1) - os.write(0x3); // mtime and atime - else - os.write(0x7); // mtime, atime and ctime - writeInt(os, javaToUnixTime(mtime)); - } - if (extra != null) // whatever not recognized - writeBytes(os, extra); - if (comment != null) //TBD: 0, Math.min(commentBytes.length, 0xffff)); - writeBytes(os, comment); - return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT; - } - - ///////////////////// LOC ////////////////////// - static Entry readLOC(ZipFileSystem zipfs, long pos) - throws IOException - { - return readLOC(zipfs, pos, new byte[1024]); - } - - static Entry readLOC(ZipFileSystem zipfs, long pos, byte[] buf) - throws IOException - { - return new Entry().loc(zipfs, pos, buf); - } - - Entry loc(ZipFileSystem zipfs, long pos, byte[] buf) - throws IOException - { - assert (buf.length >= LOCHDR); - if (zipfs.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR) - throw new ZipException("loc: reading failed"); - if (LOCSIG(buf) != LOCSIG) - throw new ZipException("loc: wrong sig ->" - + Long.toString(LOCSIG(buf), 16)); - //startPos = pos; - version = LOCVER(buf); - flag = LOCFLG(buf); - method = LOCHOW(buf); - mtime = dosToJavaTime(LOCTIM(buf)); - crc = LOCCRC(buf); - csize = LOCSIZ(buf); - size = LOCLEN(buf); - int nlen = LOCNAM(buf); - int elen = LOCEXT(buf); - - name = new byte[nlen]; - if (zipfs.readFullyAt(name, 0, nlen, pos + LOCHDR) != nlen) { - throw new ZipException("loc: name reading failed"); - } - if (elen > 0) { - extra = new byte[elen]; - if (zipfs.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen) - != elen) { - throw new ZipException("loc: ext reading failed"); - } - } - pos += (LOCHDR + nlen + elen); - if ((flag & FLAG_DATADESCR) != 0) { - // Data Descriptor - Entry e = zipfs.getEntry0(name); // get the size/csize from cen - if (e == null) - throw new ZipException("loc: name not found in cen"); - size = e.size; - csize = e.csize; - pos += (method == METHOD_STORED ? size : csize); - if (size >= ZIP64_MINVAL || csize >= ZIP64_MINVAL) - pos += 24; - else - pos += 16; - } else { - if (extra != null && - (size == ZIP64_MINVAL || csize == ZIP64_MINVAL)) { - // zip64 ext: must include both size and csize - int off = 0; - while (off + 20 < elen) { // HeaderID+DataSize+Data - int sz = SH(extra, off + 2); - if (SH(extra, off) == EXTID_ZIP64 && sz == 16) { - size = LL(extra, off + 4); - csize = LL(extra, off + 12); - break; - } - off += (sz + 4); - } - } - pos += (method == METHOD_STORED ? size : csize); - } - return this; - } - - int writeLOC(OutputStream os) - throws IOException - { - writeInt(os, LOCSIG); // LOC header signature - int version = version(); - - int nlen = (name != null) ? name.length : 0; - int elen = (extra != null) ? extra.length : 0; - int elen64 = 0; - int elenEXTT = 0; - if ((flag & FLAG_DATADESCR) != 0) { - writeShort(os, version()); // version needed to extract - writeShort(os, flag); // general purpose bit flag - writeShort(os, method); // compression method - // last modification time - writeInt(os, (int)javaToDosTime(mtime)); - // store size, uncompressed size, and crc-32 in data descriptor - // immediately following compressed entry data - writeInt(os, 0); - writeInt(os, 0); - writeInt(os, 0); - } else { - if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { - elen64 = 20; //headid(2) + size(2) + size(8) + csize(8) - writeShort(os, 45); // ver 4.5 for zip64 - } else { - writeShort(os, version()); // version needed to extract - } - writeShort(os, flag); // general purpose bit flag - writeShort(os, method); // compression method - // last modification time - writeInt(os, (int)javaToDosTime(mtime)); - writeInt(os, crc); // crc-32 - if (elen64 != 0) { - writeInt(os, ZIP64_MINVAL); - writeInt(os, ZIP64_MINVAL); - } else { - writeInt(os, csize); // compressed size - writeInt(os, size); // uncompressed size - } - } - if (atime != -1 && !isWindows) { // on unix use "ext time" - if (ctime == -1) - elenEXTT = 13; - else - elenEXTT = 17; - } - writeShort(os, name.length); - writeShort(os, elen + elen64 + elenEXTT); - writeBytes(os, name); - if (elen64 != 0) { - writeShort(os, EXTID_ZIP64); - writeShort(os, 16); - writeLong(os, size); - writeLong(os, csize); - } - 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 - writeInt(os, javaToUnixTime(mtime)); - writeInt(os, javaToUnixTime(atime)); - if (ctime != -1) - writeInt(os, javaToUnixTime(ctime)); - } - if (extra != null) { - writeBytes(os, extra); - } - return LOCHDR + name.length + elen + elen64 + elenEXTT; - } - - // Data Descriptior - int writeEXT(OutputStream os) - throws IOException - { - writeInt(os, EXTSIG); // EXT header signature - writeInt(os, crc); // crc-32 - if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { - writeLong(os, csize); - writeLong(os, size); - return 24; - } else { - writeInt(os, csize); // compressed size - writeInt(os, size); // uncompressed size - return 16; - } - } - - // read NTFS, UNIX and ZIP64 data from cen.extra - void readExtra(ZipFileSystem zipfs) throws IOException { - if (extra == null) - return; - int elen = extra.length; - int off = 0; - int newOff = 0; - while (off + 4 < elen) { - // extra spec: HeaderID+DataSize+Data - int pos = off; - int tag = SH(extra, pos); - int sz = SH(extra, pos + 2); - pos += 4; - if (pos + sz > elen) // invalid data - break; - switch (tag) { - case EXTID_ZIP64 : - if (size == ZIP64_MINVAL) { - if (pos + 8 > elen) // invalid zip64 extra - break; // fields, just skip - size = LL(extra, pos); - pos += 8; - } - if (csize == ZIP64_MINVAL) { - if (pos + 8 > elen) - break; - csize = LL(extra, pos); - pos += 8; - } - if (locoff == ZIP64_MINVAL) { - if (pos + 8 > elen) - break; - locoff = LL(extra, pos); - pos += 8; - } - break; - case EXTID_NTFS: - pos += 4; // reserved 4 bytes - if (SH(extra, pos) != 0x0001) - break; - if (SH(extra, pos + 2) != 24) - break; - // override the loc field, datatime here is - // more "accurate" - mtime = winToJavaTime(LL(extra, pos + 4)); - atime = winToJavaTime(LL(extra, pos + 12)); - ctime = winToJavaTime(LL(extra, pos + 20)); - break; - case EXTID_EXTT: - // spec says the Extened timestamp in cen only has mtime - // need to read the loc to get the extra a/ctime - byte[] buf = new byte[LOCHDR]; - if (zipfs.readFullyAt(buf, 0, buf.length , locoff) - != buf.length) - throw new ZipException("loc: reading failed"); - if (LOCSIG(buf) != LOCSIG) - throw new ZipException("loc: wrong sig ->" - + Long.toString(LOCSIG(buf), 16)); - - int locElen = LOCEXT(buf); - if (locElen < 9) // EXTT is at lease 9 bytes - break; - int locNlen = LOCNAM(buf); - buf = new byte[locElen]; - if (zipfs.readFullyAt(buf, 0, buf.length , locoff + LOCHDR + locNlen) - != buf.length) - throw new ZipException("loc extra: reading failed"); - int locPos = 0; - while (locPos + 4 < buf.length) { - int locTag = SH(buf, locPos); - int locSZ = SH(buf, locPos + 2); - locPos += 4; - if (locTag != EXTID_EXTT) { - locPos += locSZ; - continue; - } - int flag = CH(buf, locPos++); - if ((flag & 0x1) != 0) { - mtime = unixToJavaTime(LG(buf, locPos)); - locPos += 4; - } - if ((flag & 0x2) != 0) { - atime = unixToJavaTime(LG(buf, locPos)); - locPos += 4; - } - if ((flag & 0x4) != 0) { - ctime = unixToJavaTime(LG(buf, locPos)); - locPos += 4; - } - break; - } - break; - default: // unknown tag - System.arraycopy(extra, off, extra, newOff, sz + 4); - newOff += (sz + 4); - } - off += (sz + 4); - } - if (newOff != 0 && newOff != extra.length) - extra = Arrays.copyOf(extra, newOff); - else - extra = null; - } - } - - private static class ExChannelCloser { - Path path; - SeekableByteChannel ch; - Set streams; - ExChannelCloser(Path path, - SeekableByteChannel ch, - Set streams) - { - this.path = path; - this.ch = ch; - this.streams = streams; - } - } - - // ZIP directory has two issues: - // (1) ZIP spec does not require the ZIP file to include - // directory entry - // (2) all entries are not stored/organized in a "tree" - // structure. - // A possible solution is to build the node tree ourself as - // implemented below. - private IndexNode root; - - private void addToTree(IndexNode inode, HashSet dirs) { - if (dirs.contains(inode)) { - return; - } - IndexNode parent; - byte[] name = inode.name; - byte[] pname = getParent(name); - if (inodes.containsKey(LOOKUPKEY.as(pname))) { - parent = inodes.get(LOOKUPKEY); - } else { // pseudo directory entry - parent = new IndexNode(pname, -1); - inodes.put(parent, parent); - } - addToTree(parent, dirs); - inode.sibling = parent.child; - parent.child = inode; - if (name[name.length -1] == '/') - dirs.add(inode); - } - - private void removeFromTree(IndexNode inode) { - IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name))); - IndexNode child = parent.child; - if (child == inode) { - parent.child = child.sibling; - } else { - IndexNode last = child; - while ((child = child.sibling) != null) { - if (child == inode) { - last.sibling = child.sibling; - break; - } else { - last = child; - } - } - } - } - - private void buildNodeTree() throws IOException { - beginWrite(); - try { - HashSet dirs = new HashSet<>(); - IndexNode root = new IndexNode(ROOTPATH, -1); - inodes.put(root, root); - dirs.add(root); - for (IndexNode node : inodes.keySet().toArray(new IndexNode[0])) { - addToTree(node, dirs); - } - } finally { - endWrite(); - } - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystemProvider.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystemProvider.java deleted file mode 100644 index 97e45f9..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipFileSystemProvider.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.channels.AsynchronousFileChannel; -import java.nio.channels.FileChannel; -import java.nio.channels.SeekableByteChannel; -import java.nio.file.AccessMode; -import java.nio.file.CopyOption; -import java.nio.file.DirectoryStream; -import java.nio.file.DirectoryStream.Filter; -import java.nio.file.FileStore; -import java.nio.file.FileSystem; -import java.nio.file.FileSystemAlreadyExistsException; -import java.nio.file.FileSystemNotFoundException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.ProviderMismatchException; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileAttribute; -import java.nio.file.attribute.FileAttributeView; -import java.nio.file.spi.FileSystemProvider; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.zip.ZipError; - -/* - * - * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal - */ - -public class ZipFileSystemProvider extends FileSystemProvider { - - - private final Map filesystems = new HashMap<>(); - - public ZipFileSystemProvider() {} - - @Override - public String getScheme() { - return "zipfs"; - } - - protected Path uriToPath(URI uri) { - String scheme = uri.getScheme(); - if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { - throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); - } - try { - // only support legacy JAR URL syntax jar:{uri}!/{entry} for now - String spec = uri.getRawSchemeSpecificPart(); - int sep = spec.indexOf("!/"); - if (sep != -1) - spec = spec.substring(0, sep); - return Paths.get(new URI(spec)).toAbsolutePath(); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private boolean ensureFile(Path path) { - try { - BasicFileAttributes attrs = - Files.readAttributes(path, BasicFileAttributes.class); - if (!attrs.isRegularFile()) - throw new UnsupportedOperationException(); - return true; - } catch (IOException ioe) { - return false; - } - } - - @Override - public FileSystem newFileSystem(URI uri, Map env) - throws IOException - { - Path path = uriToPath(uri); - synchronized(filesystems) { - Path realPath = null; - if (ensureFile(path)) { - realPath = path.toRealPath(); - if (filesystems.containsKey(realPath)) - throw new FileSystemAlreadyExistsException(); - } - ZipFileSystem zipfs = null; - try { - zipfs = new ZipFileSystem(this, path, env); - } catch (ZipError ze) { - String pname = path.toString(); - if (pname.endsWith(".zip") || pname.endsWith(".jar")) - throw ze; - // assume NOT a zip/jar file - throw new UnsupportedOperationException(); - } - filesystems.put(realPath, zipfs); - return zipfs; - } - } - - @Override - public FileSystem newFileSystem(Path path, Map env) - throws IOException - { - if (path.getFileSystem() != FileSystems.getDefault()) { - throw new UnsupportedOperationException(); - } - ensureFile(path); - try { - return new ZipFileSystem(this, path, env); - } catch (ZipError ze) { - String pname = path.toString(); - if (pname.endsWith(".zip") || pname.endsWith(".jar")) - throw ze; - throw new UnsupportedOperationException(); - } - } - - @Override - public Path getPath(URI uri) { - - String spec = uri.getSchemeSpecificPart(); - int sep = spec.indexOf("!/"); - if (sep == -1) - throw new IllegalArgumentException("URI: " - + uri - + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR"); - return getFileSystem(uri).getPath(spec.substring(sep + 1)); - } - - - @Override - public FileSystem getFileSystem(URI uri) { - synchronized (filesystems) { - ZipFileSystem zipfs = null; - try { - zipfs = filesystems.get(uriToPath(uri).toRealPath()); - } catch (IOException x) { - // ignore the ioe from toRealPath(), return FSNFE - } - if (zipfs == null) - throw new FileSystemNotFoundException(); - return zipfs; - } - } - - // Checks that the given file is a UnixPath - public static final ZipPath toZipPath(Path path) { - if (path == null) - throw new NullPointerException(); - if (!(path instanceof ZipPath)) - throw new ProviderMismatchException(); - return (ZipPath)path; - } - - @Override - public void checkAccess(Path path, AccessMode... modes) throws IOException { - toZipPath(path).checkAccess(modes); - } - - @Override - public void copy(Path src, Path target, CopyOption... options) - throws IOException - { - toZipPath(src).copy(toZipPath(target), options); - } - - @Override - public void createDirectory(Path path, FileAttribute... attrs) - throws IOException - { - toZipPath(path).createDirectory(attrs); - } - - @Override - public final void delete(Path path) throws IOException { - toZipPath(path).delete(); - } - - @Override - @SuppressWarnings("unchecked") - public V - getFileAttributeView(Path path, Class type, LinkOption... options) - { - return ZipFileAttributeView.get(toZipPath(path), type); - } - - @Override - public FileStore getFileStore(Path path) throws IOException { - return toZipPath(path).getFileStore(); - } - - @Override - public boolean isHidden(Path path) { - return toZipPath(path).isHidden(); - } - - @Override - public boolean isSameFile(Path path, Path other) throws IOException { - return toZipPath(path).isSameFile(other); - } - - @Override - public void move(Path src, Path target, CopyOption... options) - throws IOException - { - toZipPath(src).move(toZipPath(target), options); - } - - @Override - public AsynchronousFileChannel newAsynchronousFileChannel(Path path, - Set options, - ExecutorService exec, - FileAttribute... attrs) - throws IOException - { - throw new UnsupportedOperationException(); - } - - @Override - public SeekableByteChannel newByteChannel(Path path, - Set options, - FileAttribute... attrs) - throws IOException - { - return toZipPath(path).newByteChannel(options, attrs); - } - - @Override - public DirectoryStream newDirectoryStream( - Path path, Filter filter) throws IOException - { - return toZipPath(path).newDirectoryStream(filter); - } - - @Override - public FileChannel newFileChannel(Path path, - Set options, - FileAttribute... attrs) - throws IOException - { - return toZipPath(path).newFileChannel(options, attrs); - } - - @Override - public InputStream newInputStream(Path path, OpenOption... options) - throws IOException - { - return toZipPath(path).newInputStream(options); - } - - @Override - public OutputStream newOutputStream(Path path, OpenOption... options) - throws IOException - { - return toZipPath(path).newOutputStream(options); - } - - @Override - public A - readAttributes(Path path, Class type, LinkOption... options) - throws IOException - { - if (type == BasicFileAttributes.class || type == ZipFileAttributes.class) - return (A)toZipPath(path).getAttributes(); - return null; - } - - @Override - public Map - readAttributes(Path path, String attribute, LinkOption... options) - throws IOException - { - return toZipPath(path).readAttributes(attribute, options); - } - - @Override - public Path readSymbolicLink(Path link) throws IOException { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public void setAttribute(Path path, String attribute, - Object value, LinkOption... options) - throws IOException - { - toZipPath(path).setAttribute(attribute, value, options); - } - - ////////////////////////////////////////////////////////////// - void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { - synchronized (filesystems) { - zfpath = zfpath.toRealPath(); - if (filesystems.get(zfpath) == zfs) - filesystems.remove(zfpath); - } - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipPath.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipPath.java deleted file mode 100644 index 4de224b..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipPath.java +++ /dev/null @@ -1,901 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static java.nio.file.StandardOpenOption.CREATE_NEW; -import static java.nio.file.StandardOpenOption.READ; -import static java.nio.file.StandardOpenOption.WRITE; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.nio.channels.FileChannel; -import java.nio.channels.SeekableByteChannel; -import java.nio.file.AccessDeniedException; -import java.nio.file.AccessMode; -import java.nio.file.CopyOption; -import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.DirectoryStream; -import java.nio.file.DirectoryStream.Filter; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.FileStore; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.LinkOption; -import java.nio.file.NoSuchFileException; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.ProviderMismatchException; -import java.nio.file.ReadOnlyFileSystemException; -import java.nio.file.WatchEvent; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; -import java.nio.file.attribute.BasicFileAttributeView; -import java.nio.file.attribute.FileAttribute; -import java.nio.file.attribute.FileTime; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - - -/** - * - * @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal - */ - -public class ZipPath implements Path { - - private final ZipFileSystem zfs; - private final byte[] path; - private volatile int[] offsets; - private int hashcode = 0; // cached hashcode (created lazily) - - ZipPath(ZipFileSystem zfs, byte[] path) { - this(zfs, path, false); - } - - ZipPath(ZipFileSystem zfs, byte[] path, boolean normalized) - { - this.zfs = zfs; - if (normalized) - this.path = path; - else - this.path = normalize(path); - } - - @Override - public ZipPath getRoot() { - if (this.isAbsolute()) - return new ZipPath(zfs, new byte[]{path[0]}); - else - return null; - } - - @Override - public Path getFileName() { - initOffsets(); - int count = offsets.length; - if (count == 0) - return null; // no elements so no name - if (count == 1 && path[0] != '/') - return this; - int lastOffset = offsets[count-1]; - int len = path.length - lastOffset; - byte[] result = new byte[len]; - System.arraycopy(path, lastOffset, result, 0, len); - return new ZipPath(zfs, result); - } - - @Override - public ZipPath getParent() { - initOffsets(); - int count = offsets.length; - if (count == 0) // no elements so no parent - return null; - int len = offsets[count-1] - 1; - if (len <= 0) // parent is root only (may be null) - return getRoot(); - byte[] result = new byte[len]; - System.arraycopy(path, 0, result, 0, len); - return new ZipPath(zfs, result); - } - - @Override - public int getNameCount() { - initOffsets(); - return offsets.length; - } - - @Override - public ZipPath getName(int index) { - initOffsets(); - if (index < 0 || index >= offsets.length) - throw new IllegalArgumentException(); - int begin = offsets[index]; - int len; - if (index == (offsets.length-1)) - len = path.length - begin; - else - len = offsets[index+1] - begin - 1; - // construct result - byte[] result = new byte[len]; - System.arraycopy(path, begin, result, 0, len); - return new ZipPath(zfs, result); - } - - @Override - public ZipPath subpath(int beginIndex, int endIndex) { - initOffsets(); - if (beginIndex < 0 || - beginIndex >= offsets.length || - endIndex > offsets.length || - beginIndex >= endIndex) - throw new IllegalArgumentException(); - - // starting offset and length - int begin = offsets[beginIndex]; - int len; - if (endIndex == offsets.length) - len = path.length - begin; - else - len = offsets[endIndex] - begin - 1; - // construct result - byte[] result = new byte[len]; - System.arraycopy(path, begin, result, 0, len); - return new ZipPath(zfs, result); - } - - @Override - public ZipPath toRealPath(LinkOption... options) throws IOException { - ZipPath realPath = new ZipPath(zfs, getResolvedPath()).toAbsolutePath(); - realPath.checkAccess(); - return realPath; - } - - boolean isHidden() { - return false; - } - - @Override - public ZipPath toAbsolutePath() { - if (isAbsolute()) { - return this; - } else { - //add / bofore the existing path - byte[] defaultdir = zfs.getDefaultDir().path; - int defaultlen = defaultdir.length; - boolean endsWith = (defaultdir[defaultlen - 1] == '/'); - byte[] t = null; - if (endsWith) - t = new byte[defaultlen + path.length]; - else - t = new byte[defaultlen + 1 + path.length]; - System.arraycopy(defaultdir, 0, t, 0, defaultlen); - if (!endsWith) - t[defaultlen++] = '/'; - System.arraycopy(path, 0, t, defaultlen, path.length); - return new ZipPath(zfs, t, true); // normalized - } - } - - @Override - public URI toUri() { - try { - return new URI(zfs.provider().getScheme(), - zfs.getZipFile().toUri() + - "!" + - zfs.getString(toAbsolutePath().path), - null); - } catch (Exception ex) { - throw new AssertionError(ex); - } - } - - private boolean equalsNameAt(ZipPath other, int index) { - int mbegin = offsets[index]; - int mlen = 0; - if (index == (offsets.length-1)) - mlen = path.length - mbegin; - else - mlen = offsets[index + 1] - mbegin - 1; - int obegin = other.offsets[index]; - int olen = 0; - if (index == (other.offsets.length - 1)) - olen = other.path.length - obegin; - else - olen = other.offsets[index + 1] - obegin - 1; - if (mlen != olen) - return false; - int n = 0; - while(n < mlen) { - if (path[mbegin + n] != other.path[obegin + n]) - return false; - n++; - } - return true; - } - - @Override - public Path relativize(Path other) { - final ZipPath o = checkPath(other); - if (o.equals(this)) - return new ZipPath(getFileSystem(), new byte[0], true); - if (/* this.getFileSystem() != o.getFileSystem() || */ - this.isAbsolute() != o.isAbsolute()) { - throw new IllegalArgumentException(); - } - int mc = this.getNameCount(); - int oc = o.getNameCount(); - int n = Math.min(mc, oc); - int i = 0; - while (i < n) { - if (!equalsNameAt(o, i)) - break; - i++; - } - int dotdots = mc - i; - int len = dotdots * 3 - 1; - if (i < oc) - len += (o.path.length - o.offsets[i] + 1); - byte[] result = new byte[len]; - - int pos = 0; - while (dotdots > 0) { - result[pos++] = (byte)'.'; - result[pos++] = (byte)'.'; - if (pos < len) // no tailing slash at the end - result[pos++] = (byte)'/'; - dotdots--; - } - if (i < oc) - System.arraycopy(o.path, o.offsets[i], - result, pos, - o.path.length - o.offsets[i]); - return new ZipPath(getFileSystem(), result); - } - - @Override - public ZipFileSystem getFileSystem() { - return zfs; - } - - @Override - public boolean isAbsolute() { - return (this.path.length > 0 && path[0] == '/'); - } - - @Override - public ZipPath resolve(Path other) { - final ZipPath o = checkPath(other); - if (o.isAbsolute()) - return o; - byte[] resolved = null; - if (this.path[path.length - 1] == '/') { - resolved = new byte[path.length + o.path.length]; - System.arraycopy(path, 0, resolved, 0, path.length); - System.arraycopy(o.path, 0, resolved, path.length, o.path.length); - } else { - resolved = new byte[path.length + 1 + o.path.length]; - System.arraycopy(path, 0, resolved, 0, path.length); - resolved[path.length] = '/'; - System.arraycopy(o.path, 0, resolved, path.length + 1, o.path.length); - } - return new ZipPath(zfs, resolved); - } - - @Override - public Path resolveSibling(Path other) { - if (other == null) - throw new NullPointerException(); - Path parent = getParent(); - return (parent == null) ? other : parent.resolve(other); - } - - @Override - public boolean startsWith(Path other) { - final ZipPath o = checkPath(other); - if (o.isAbsolute() != this.isAbsolute() || - o.path.length > this.path.length) - return false; - int olast = o.path.length; - for (int i = 0; i < olast; i++) { - if (o.path[i] != this.path[i]) - return false; - } - olast--; - return o.path.length == this.path.length || - o.path[olast] == '/' || - this.path[olast + 1] == '/'; - } - - @Override - public boolean endsWith(Path other) { - final ZipPath o = checkPath(other); - int olast = o.path.length - 1; - if (olast > 0 && o.path[olast] == '/') - olast--; - int last = this.path.length - 1; - if (last > 0 && this.path[last] == '/') - last--; - if (olast == -1) // o.path.length == 0 - return last == -1; - if ((o.isAbsolute() &&(!this.isAbsolute() || olast != last)) || - (last < olast)) - return false; - for (; olast >= 0; olast--, last--) { - if (o.path[olast] != this.path[last]) - return false; - } - return o.path[olast + 1] == '/' || - last == -1 || this.path[last] == '/'; - } - - @Override - public ZipPath resolve(String other) { - return resolve(getFileSystem().getPath(other)); - } - - @Override - public final Path resolveSibling(String other) { - return resolveSibling(getFileSystem().getPath(other)); - } - - @Override - public final boolean startsWith(String other) { - return startsWith(getFileSystem().getPath(other)); - } - - @Override - public final boolean endsWith(String other) { - return endsWith(getFileSystem().getPath(other)); - } - - @Override - public Path normalize() { - byte[] resolved = getResolved(); - if (resolved == path) // no change - return this; - return new ZipPath(zfs, resolved, true); - } - - private ZipPath checkPath(Path path) { - if (path == null) - throw new NullPointerException(); - if (!(path instanceof ZipPath)) - throw new ProviderMismatchException(); - return (ZipPath) path; - } - - // create offset list if not already created - private void initOffsets() { - if (offsets == null) { - int count, index; - // count names - count = 0; - index = 0; - while (index < path.length) { - byte c = path[index++]; - if (c != '/') { - count++; - while (index < path.length && path[index] != '/') - index++; - } - } - // populate offsets - int[] result = new int[count]; - count = 0; - index = 0; - while (index < path.length) { - byte c = path[index]; - if (c == '/') { - index++; - } else { - result[count++] = index++; - while (index < path.length && path[index] != '/') - index++; - } - } - synchronized (this) { - if (offsets == null) - offsets = result; - } - } - } - - // resolved path for locating zip entry inside the zip file, - // the result path does not contain ./ and .. components - private volatile byte[] resolved = null; - byte[] getResolvedPath() { - byte[] r = resolved; - if (r == null) { - if (isAbsolute()) - r = getResolved(); - else - r = toAbsolutePath().getResolvedPath(); - if (r[0] == '/') - r = Arrays.copyOfRange(r, 1, r.length); - resolved = r; - } - return resolved; - } - - // removes redundant slashs, replace "\" to zip separator "/" - // and check for invalid characters - private byte[] normalize(byte[] path) { - if (path.length == 0) - return path; - byte prevC = 0; - for (int i = 0; i < path.length; i++) { - byte c = path[i]; - if (c == '\\') - return normalize(path, i); - if (c == (byte)'/' && prevC == '/') - return normalize(path, i - 1); - if (c == '\u0000') - throw new InvalidPathException(zfs.getString(path), - "Path: nul character not allowed"); - prevC = c; - } - return path; - } - - private byte[] normalize(byte[] path, int off) { - byte[] to = new byte[path.length]; - int n = 0; - while (n < off) { - to[n] = path[n]; - n++; - } - int m = n; - byte prevC = 0; - while (n < path.length) { - byte c = path[n++]; - if (c == (byte)'\\') - c = (byte)'/'; - if (c == (byte)'/' && prevC == (byte)'/') - continue; - if (c == '\u0000') - throw new InvalidPathException(zfs.getString(path), - "Path: nul character not allowed"); - to[m++] = c; - prevC = c; - } - if (m > 1 && to[m - 1] == '/') - m--; - return (m == to.length)? to : Arrays.copyOf(to, m); - } - - // Remove DotSlash(./) and resolve DotDot (..) components - private byte[] getResolved() { - if (path.length == 0) - return path; - for (int i = 0; i < path.length; i++) { - byte c = path[i]; - if (c == (byte)'.') - return resolve0(); - } - return path; - } - - // TBD: performance, avoid initOffsets - private byte[] resolve0() { - byte[] to = new byte[path.length]; - int nc = getNameCount(); - int[] lastM = new int[nc]; - int lastMOff = -1; - int m = 0; - for (int i = 0; i < nc; i++) { - int n = offsets[i]; - int len = (i == offsets.length - 1)? - (path.length - n):(offsets[i + 1] - n - 1); - if (len == 1 && path[n] == (byte)'.') { - if (m == 0 && path[0] == '/') // absolute path - to[m++] = '/'; - continue; - } - if (len == 2 && path[n] == '.' && path[n + 1] == '.') { - if (lastMOff >= 0) { - m = lastM[lastMOff--]; // retreat - continue; - } - if (path[0] == '/') { // "/../xyz" skip - if (m == 0) - to[m++] = '/'; - } else { // "../xyz" -> "../xyz" - if (m != 0 && to[m-1] != '/') - to[m++] = '/'; - while (len-- > 0) - to[m++] = path[n++]; - } - continue; - } - if (m == 0 && path[0] == '/' || // absolute path - m != 0 && to[m-1] != '/') { // not the first name - to[m++] = '/'; - } - lastM[++lastMOff] = m; - while (len-- > 0) - to[m++] = path[n++]; - } - if (m > 1 && to[m - 1] == '/') - m--; - return (m == to.length)? to : Arrays.copyOf(to, m); - } - - @Override - public String toString() { - return zfs.getString(path); - } - - @Override - public int hashCode() { - int h = hashcode; - if (h == 0) - hashcode = h = Arrays.hashCode(path); - return h; - } - - @Override - public boolean equals(Object obj) { - return obj != null && - obj instanceof ZipPath && - this.zfs == ((ZipPath)obj).zfs && - compareTo((Path) obj) == 0; - } - - @Override - public int compareTo(Path other) { - final ZipPath o = checkPath(other); - int len1 = this.path.length; - int len2 = o.path.length; - - int n = Math.min(len1, len2); - byte v1[] = this.path; - byte v2[] = o.path; - - int k = 0; - while (k < n) { - int c1 = v1[k] & 0xff; - int c2 = v2[k] & 0xff; - if (c1 != c2) - return c1 - c2; - k++; - } - return len1 - len2; - } - - public WatchKey register( - WatchService watcher, - WatchEvent.Kind[] events, - WatchEvent.Modifier... modifiers) { - if (watcher == null || events == null || modifiers == null) { - throw new NullPointerException(); - } - throw new UnsupportedOperationException(); - } - - @Override - public WatchKey register(WatchService watcher, WatchEvent.Kind... events) { - return register(watcher, events, new WatchEvent.Modifier[0]); - } - - @Override - public final File toFile() { - throw new UnsupportedOperationException(); - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return (i < getNameCount()); - } - - @Override - public Path next() { - if (i < getNameCount()) { - Path result = getName(i); - i++; - return result; - } else { - throw new NoSuchElementException(); - } - } - - @Override - public void remove() { - throw new ReadOnlyFileSystemException(); - } - }; - } - - ///////////////////////////////////////////////////////////////////// - - - void createDirectory(FileAttribute... attrs) - throws IOException - { - zfs.createDirectory(getResolvedPath(), attrs); - } - - InputStream newInputStream(OpenOption... options) throws IOException - { - if (options.length > 0) { - for (OpenOption opt : options) { - if (opt != READ) - throw new UnsupportedOperationException("'" + opt + "' not allowed"); - } - } - return zfs.newInputStream(getResolvedPath()); - } - - DirectoryStream newDirectoryStream(Filter filter) - throws IOException - { - return new ZipDirectoryStream(this, filter); - } - - void delete() throws IOException { - zfs.deleteFile(getResolvedPath(), true); - } - - void deleteIfExists() throws IOException { - zfs.deleteFile(getResolvedPath(), false); - } - - ZipFileAttributes getAttributes() throws IOException - { - ZipFileAttributes zfas = zfs.getFileAttributes(getResolvedPath()); - if (zfas == null) - throw new NoSuchFileException(toString()); - return zfas; - } - - void setAttribute(String attribute, Object value, LinkOption... options) - throws IOException - { - String type = null; - String attr = null; - int colonPos = attribute.indexOf(':'); - if (colonPos == -1) { - type = "basic"; - attr = attribute; - } else { - type = attribute.substring(0, colonPos++); - attr = attribute.substring(colonPos); - } - ZipFileAttributeView view = ZipFileAttributeView.get(this, type); - if (view == null) - throw new UnsupportedOperationException("view <" + view + "> is not supported"); - view.setAttribute(attr, value); - } - - void setTimes(FileTime mtime, FileTime atime, FileTime ctime) - throws IOException - { - zfs.setTimes(getResolvedPath(), mtime, atime, ctime); - } - - Map readAttributes(String attributes, LinkOption... options) - throws IOException - - { - String view = null; - String attrs = null; - int colonPos = attributes.indexOf(':'); - if (colonPos == -1) { - view = "basic"; - attrs = attributes; - } else { - view = attributes.substring(0, colonPos++); - attrs = attributes.substring(colonPos); - } - ZipFileAttributeView zfv = ZipFileAttributeView.get(this, view); - if (zfv == null) { - throw new UnsupportedOperationException("view not supported"); - } - return zfv.readAttributes(attrs); - } - - FileStore getFileStore() throws IOException { - // each ZipFileSystem only has one root (as requested for now) - if (exists()) - return zfs.getFileStore(this); - throw new NoSuchFileException(zfs.getString(path)); - } - - boolean isSameFile(Path other) throws IOException { - if (this.equals(other)) - return true; - if (other == null || - this.getFileSystem() != other.getFileSystem()) - return false; - this.checkAccess(); - ((ZipPath)other).checkAccess(); - return Arrays.equals(this.getResolvedPath(), - ((ZipPath)other).getResolvedPath()); - } - - SeekableByteChannel newByteChannel(Set options, - FileAttribute... attrs) - throws IOException - { - return zfs.newByteChannel(getResolvedPath(), options, attrs); - } - - - FileChannel newFileChannel(Set options, - FileAttribute... attrs) - throws IOException - { - return zfs.newFileChannel(getResolvedPath(), options, attrs); - } - - void checkAccess(AccessMode... modes) throws IOException { - boolean w = false; - boolean x = false; - for (AccessMode mode : modes) { - switch (mode) { - case READ: - break; - case WRITE: - w = true; - break; - case EXECUTE: - x = true; - break; - default: - throw new UnsupportedOperationException(); - } - } - ZipFileAttributes attrs = zfs.getFileAttributes(getResolvedPath()); - if (attrs == null && (path.length != 1 || path[0] != '/')) - throw new NoSuchFileException(toString()); - if (w) { - if (zfs.isReadOnly()) - throw new AccessDeniedException(toString()); - } - if (x) - throw new AccessDeniedException(toString()); - } - - boolean exists() { - if (path.length == 1 && path[0] == '/') - return true; - try { - return zfs.exists(getResolvedPath()); - } catch (IOException x) {} - return false; - } - - OutputStream newOutputStream(OpenOption... options) throws IOException - { - if (options.length == 0) - return zfs.newOutputStream(getResolvedPath(), - CREATE_NEW, WRITE); - return zfs.newOutputStream(getResolvedPath(), options); - } - - void move(ZipPath target, CopyOption... options) - throws IOException - { - if (Files.isSameFile(this.zfs.getZipFile(), target.zfs.getZipFile())) - { - zfs.copyFile(true, - getResolvedPath(), target.getResolvedPath(), - options); - } else { - copyToTarget(target, options); - delete(); - } - } - - void copy(ZipPath target, CopyOption... options) - throws IOException - { - if (Files.isSameFile(this.zfs.getZipFile(), target.zfs.getZipFile())) - zfs.copyFile(false, - getResolvedPath(), target.getResolvedPath(), - options); - else - copyToTarget(target, options); - } - - private void copyToTarget(ZipPath target, CopyOption... options) - throws IOException - { - boolean replaceExisting = false; - boolean copyAttrs = false; - for (CopyOption opt : options) { - if (opt == REPLACE_EXISTING) - replaceExisting = true; - else if (opt == COPY_ATTRIBUTES) - copyAttrs = true; - } - // attributes of source file - ZipFileAttributes zfas = getAttributes(); - // check if target exists - boolean exists; - if (replaceExisting) { - try { - target.deleteIfExists(); - exists = false; - } catch (DirectoryNotEmptyException x) { - exists = true; - } - } else { - exists = target.exists(); - } - if (exists) - throw new FileAlreadyExistsException(target.toString()); - - if (zfas.isDirectory()) { - // create directory or file - target.createDirectory(); - } else { - InputStream is = zfs.newInputStream(getResolvedPath()); - try { - OutputStream os = target.newOutputStream(); - try { - byte[] buf = new byte[8192]; - int n = 0; - while ((n = is.read(buf)) != -1) { - os.write(buf, 0, n); - } - } finally { - os.close(); - } - } finally { - is.close(); - } - } - if (copyAttrs) { - BasicFileAttributeView view = - ZipFileAttributeView.get(target, BasicFileAttributeView.class); - try { - view.setTimes(zfas.lastModifiedTime(), - zfas.lastAccessTime(), - zfas.creationTime()); - } catch (IOException x) { - // rollback? - try { - target.delete(); - } catch (IOException ignore) { } - throw x; - } - } - } -} diff --git a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipUtils.java b/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipUtils.java deleted file mode 100644 index 64d8c47..0000000 --- a/src/java.compiler/share/classes/com/itsaky/androidide/zipfs/ZipUtils.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - -package com.itsaky.androidide.zipfs; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.regex.PatternSyntaxException; - -/** - * - * @author Xueming Shen - */ - -class ZipUtils { - - /* - * Writes a 16-bit short to the output stream in little-endian byte order. - */ - public static void writeShort(OutputStream os, int v) throws IOException { - os.write(v & 0xff); - os.write((v >>> 8) & 0xff); - } - - /* - * Writes a 32-bit int to the output stream in little-endian byte order. - */ - public static void writeInt(OutputStream os, long v) throws IOException { - os.write((int)(v & 0xff)); - os.write((int)((v >>> 8) & 0xff)); - os.write((int)((v >>> 16) & 0xff)); - os.write((int)((v >>> 24) & 0xff)); - } - - /* - * Writes a 64-bit int to the output stream in little-endian byte order. - */ - public static void writeLong(OutputStream os, long v) throws IOException { - os.write((int)(v & 0xff)); - os.write((int)((v >>> 8) & 0xff)); - os.write((int)((v >>> 16) & 0xff)); - os.write((int)((v >>> 24) & 0xff)); - os.write((int)((v >>> 32) & 0xff)); - os.write((int)((v >>> 40) & 0xff)); - os.write((int)((v >>> 48) & 0xff)); - os.write((int)((v >>> 56) & 0xff)); - } - - /* - * Writes an array of bytes to the output stream. - */ - public static void writeBytes(OutputStream os, byte[] b) - throws IOException - { - os.write(b, 0, b.length); - } - - /* - * Writes an array of bytes to the output stream. - */ - public static void writeBytes(OutputStream os, byte[] b, int off, int len) - throws IOException - { - os.write(b, off, len); - } - - /* - * Append a slash at the end, if it does not have one yet - */ - public static byte[] toDirectoryPath(byte[] dir) { - if (dir.length != 0 && dir[dir.length - 1] != '/') { - dir = Arrays.copyOf(dir, dir.length + 1); - dir[dir.length - 1] = '/'; - } - return dir; - } - - /* - * Converts DOS time to Java time (number of milliseconds since epoch). - */ - public static long dosToJavaTime(long dtime) { - 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. - */ - 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; - } - - - // used to adjust values between Windows and java epoch - private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L; - public static final long winToJavaTime(long wtime) { - return TimeUnit.MILLISECONDS.convert( - wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS); - } - - public static final long javaToWinTime(long time) { - return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS) - - WINDOWS_EPOCH_IN_MICROSECONDS) * 10; - } - - public static final long unixToJavaTime(long utime) { - return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS); - } - - public static final long javaToUnixTime(long time) { - return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS); - } - - private static final String regexMetaChars = ".^$+{[]|()"; - private static final String globMetaChars = "\\*?[{"; - private static boolean isRegexMeta(char c) { - return regexMetaChars.indexOf(c) != -1; - } - private static boolean isGlobMeta(char c) { - return globMetaChars.indexOf(c) != -1; - } - private static char EOL = 0; //TBD - private static char next(String glob, int i) { - if (i < glob.length()) { - return glob.charAt(i); - } - return EOL; - } - - /* - * Creates a regex pattern from the given glob expression. - * - * @throws PatternSyntaxException - */ - public static String toRegexPattern(String globPattern) { - boolean inGroup = false; - StringBuilder regex = new StringBuilder("^"); - - int i = 0; - while (i < globPattern.length()) { - char c = globPattern.charAt(i++); - switch (c) { - case '\\': - // escape special characters - if (i == globPattern.length()) { - throw new PatternSyntaxException("No character to escape", - globPattern, i - 1); - } - char next = globPattern.charAt(i++); - if (isGlobMeta(next) || isRegexMeta(next)) { - regex.append('\\'); - } - regex.append(next); - break; - case '/': - regex.append(c); - break; - case '[': - // don't match name separator in class - regex.append("[[^/]&&["); - if (next(globPattern, i) == '^') { - // escape the regex negation char if it appears - regex.append("\\^"); - i++; - } else { - // negation - if (next(globPattern, i) == '!') { - regex.append('^'); - i++; - } - // hyphen allowed at start - if (next(globPattern, i) == '-') { - regex.append('-'); - i++; - } - } - boolean hasRangeStart = false; - char last = 0; - while (i < globPattern.length()) { - c = globPattern.charAt(i++); - if (c == ']') { - break; - } - if (c == '/') { - throw new PatternSyntaxException("Explicit 'name separator' in class", - globPattern, i - 1); - } - // TBD: how to specify ']' in a class? - if (c == '\\' || c == '[' || - c == '&' && next(globPattern, i) == '&') { - // escape '\', '[' or "&&" for regex class - regex.append('\\'); - } - regex.append(c); - - if (c == '-') { - if (!hasRangeStart) { - throw new PatternSyntaxException("Invalid range", - globPattern, i - 1); - } - if ((c = next(globPattern, i++)) == EOL || c == ']') { - break; - } - if (c < last) { - throw new PatternSyntaxException("Invalid range", - globPattern, i - 3); - } - regex.append(c); - hasRangeStart = false; - } else { - hasRangeStart = true; - last = c; - } - } - if (c != ']') { - throw new PatternSyntaxException("Missing ']", globPattern, i - 1); - } - regex.append("]]"); - break; - case '{': - if (inGroup) { - throw new PatternSyntaxException("Cannot nest groups", - globPattern, i - 1); - } - regex.append("(?:(?:"); - inGroup = true; - break; - case '}': - if (inGroup) { - regex.append("))"); - inGroup = false; - } else { - regex.append('}'); - } - break; - case ',': - if (inGroup) { - regex.append(")|(?:"); - } else { - regex.append(','); - } - break; - case '*': - if (next(globPattern, i) == '*') { - // crosses directory boundaries - regex.append(".*"); - i++; - } else { - // within directory boundary - regex.append("[^/]*"); - } - break; - case '?': - regex.append("[^/]"); - break; - default: - if (isRegexMeta(c)) { - regex.append('\\'); - } - regex.append(c); - } - } - if (inGroup) { - throw new PatternSyntaxException("Missing '}", globPattern, i - 1); - } - return regex.append('$').toString(); - } -} diff --git a/src/java.compiler/share/classes/module-info.java b/src/java.compiler/share/classes/module-info.java index 235ca6c..1064543 100644 --- a/src/java.compiler/share/classes/module-info.java +++ b/src/java.compiler/share/classes/module-info.java @@ -37,7 +37,6 @@ */ module java.compiler { exports com.itsaky.androidide.config; - exports com.itsaky.androidide.zipfs; exports com.itsaky.androidide.zipfs2; exports javac.internal; exports javac.internal.jimage;