6989148: (fs) zip provider should be available "out of the box"
Summary: zip filesystem provider update, add zipfs.jar into ext dir
Reviewed-by: alanb
--- a/jdk/make/mkdemo/nio/zipfs/Makefile Mon Dec 06 18:52:23 2010 +0000
+++ b/jdk/make/mkdemo/nio/zipfs/Makefile Mon Dec 06 13:18:16 2010 -0800
@@ -33,8 +33,8 @@
include $(BUILDDIR)/common/Defs.gmk
DEMO_ROOT = $(SHARE_SRC)/demo/nio/$(DEMONAME)
-DEMO_TOPFILES = ./README.txt
-DEMO_SRCDIR = $(DEMO_ROOT)
+DEMO_TOPFILES = README.txt Demo.java
+DEMO_SRCDIR = $(DEMO_ROOT)/src
DEMO_DESTDIR = $(DEMODIR)/nio/$(DEMONAME)
#
@@ -42,10 +42,10 @@
#
include $(BUILDDIR)/common/Demo.gmk
-#EXTJAR = $(EXTDIR)/$(DEMONAME).jar
-#
-#all : build $(EXTJAR)
-#
-#$(EXTJAR) : $(DEMO_JAR)
-# $(prep-target)
-# $(CP) $(DEMO_JAR) $(EXTJAR)
+EXTJAR = $(EXTDIR)/$(DEMONAME).jar
+
+all : build $(EXTJAR)
+
+$(EXTJAR) : $(DEMO_JAR)
+ $(prep-target)
+ $(CP) $(DEMO_JAR) $(EXTJAR)
--- a/jdk/src/share/demo/nio/zipfs/Demo.java Mon Dec 06 18:52:23 2010 +0000
+++ b/jdk/src/share/demo/nio/zipfs/Demo.java Mon Dec 06 13:18:16 2010 -0800
@@ -45,12 +45,7 @@
/*
* ZipFileSystem usage demo
*
- * java [-cp .../zipfs.jar:./] Demo action ZipfileName [...]
- *
- * To deploy the provider, either copy the zipfs.jar into JDK/JRE
- * extensions directory or add
- * <JDK_HOME>/demo/nio/ZipFileSystem/zipfs.jar
- * into your class path as showed above.
+ * java Demo action ZipfileName [...]
*
* @author Xueming Shen
*/
@@ -153,14 +148,11 @@
Action action = Action.valueOf(args[0]);
Map<String, Object> env = env = new HashMap<>();
if (action == Action.create)
- env.put("createNew", true);
+ env.put("create", "true");
if (action == Action.tlist || action == Action.twalk)
env.put("buildDirTree", true);
+ FileSystem fs = FileSystems.newFileSystem(Paths.get(args[1]), env, null);
- FileSystem fs = FileSystems.newFileSystem(
- URI.create("zip" + Paths.get(args[1]).toUri().toString().substring(4)),
- env,
- null);
try {
FileSystem fs2;
Path path, src, dst;
@@ -207,19 +199,13 @@
src.copyTo(dst, COPY_ATTRIBUTES);
break;
case zzmove:
- fs2 = FileSystems.newFileSystem(
- URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)),
- env,
- null);
+ fs2 = FileSystems.newFileSystem(Paths.get(args[2]), env, null);
//sf1.getPath(args[3]).moveTo(fs2.getPath(args[3]));
z2zmove(fs, fs2, args[3]);
fs2.close();
break;
case zzcopy:
- fs2 = FileSystems.newFileSystem(
- URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)),
- env,
- null);
+ fs2 = FileSystems.newFileSystem(Paths.get(args[2]), env, null);
//sf1.getPath(args[3]).copyTo(fs2.getPath(args[3]));
z2zcopy(fs, fs2, args[3]);
fs2.close();
--- a/jdk/src/share/demo/nio/zipfs/META-INF/services/java.nio.file.spi.FileSystemProvider Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-com.sun.nio.zipfs.ZipFileSystemProvider
-com.sun.nio.zipfs.JarFileSystemProvider
-
--- a/jdk/src/share/demo/nio/zipfs/README.txt Mon Dec 06 18:52:23 2010 +0000
+++ b/jdk/src/share/demo/nio/zipfs/README.txt Mon Dec 06 13:18:16 2010 -0800
@@ -1,10 +1,6 @@
ZipFileSystem is a file system provider that treats the contents of a zip or
JAR file as a java.nio.file.FileSystem.
-To deploy the provider you must copy zipfs.jar into your extensions
-directory or else add <JDK_HOME>/demo/nio/zipfs/zipfs.jar
-to your class path.
-
The factory methods defined by the java.nio.file.FileSystems class can be
used to create a FileSystem, eg:
@@ -15,9 +11,9 @@
-or
- // locate file system by URI
+ // locate file system by the legacy JAR URL syntax
Map<String,?> env = Collections.emptyMap();
- URI uri = URI.create("zip:///mydir/foo.jar");
+ URI uri = URI.create("jar:file:/mydir/foo.jar");
FileSystem fs = FileSystems.newFileSystem(uri, env);
Once a FileSystem is created then classes in the java.nio.file package
@@ -26,4 +22,6 @@
Path mf = fs.getPath("/META-INF/MANIFEST.MF");
InputStream in = mf.newInputStream();
+See Demo.java for more interesting usages.
+
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/JarFileSystemProvider.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Sun Microsystems nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.sun.nio.zipfs;
-
-import java.nio.file.*;
-import java.nio.file.spi.*;
-import java.nio.file.attribute.*;
-import java.nio.file.spi.FileSystemProvider;
-
-import java.net.URI;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.nio.channels.FileChannel;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class JarFileSystemProvider extends ZipFileSystemProvider
-{
-
- @Override
- public String getScheme() {
- return "jar";
- }
-
- @Override
- protected Path uriToPath(URI uri) {
- String scheme = uri.getScheme();
- if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
- throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
- }
- try {
- String uristr = uri.toString();
- int end = uristr.indexOf("!/");
- uristr = uristr.substring(4, (end == -1) ? uristr.length() : end);
- uri = new URI(uristr);
- return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null))
- .toAbsolutePath();
- } catch (URISyntaxException e) {
- throw new AssertionError(e); //never thrown
- }
- }
-
- @Override
- public Path getPath(URI uri) {
- FileSystem fs = getFileSystem(uri);
- String path = uri.getFragment();
- if (path == null) {
- String uristr = uri.toString();
- int off = uristr.indexOf("!/");
- if (off != -1)
- path = uristr.substring(off + 2);
- }
- if (path != null)
- return fs.getPath(path);
- throw new IllegalArgumentException("URI: "
- + uri
- + " does not contain path fragment ex. jar:///c:/foo.zip!/BAR");
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipCoder.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.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<CharsetDecoder> decTL = new ThreadLocal<>();
- private final ThreadLocal<CharsetEncoder> 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;
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipConstants.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.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 b[n] & 0xff;
- }
-
- static final int SH(byte[] b, int n) {
- return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8);
- }
-
- 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;
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipDirectoryStream.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-import java.nio.file.DirectoryStream;
-import java.nio.file.ClosedDirectoryStreamException;
-import java.nio.file.NotDirectoryException;
-import java.nio.file.Path;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.io.IOException;
-
-/**
- *
- * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
- */
-
-public class ZipDirectoryStream implements DirectoryStream<Path> {
-
- private final ZipFileSystem zipfs;
- private final byte[] path;
- private final DirectoryStream.Filter<? super Path> filter;
- private volatile boolean isClosed;
- private volatile Iterator<Path> itr;
-
- ZipDirectoryStream(ZipPath zipPath,
- DirectoryStream.Filter<? super java.nio.file.Path> 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<Path> 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;
- }
-
-
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributeView.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-package com.sun.nio.zipfs;
-
-import java.nio.file.attribute.BasicFileAttributeView;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileTime;
-import java.io.IOException;
-import java.util.LinkedHashMap;
-
-/*
- * @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 extends FileAttributeView> V get(ZipPath path, Class<V> 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");
- }
-
- public Object getAttribute(String attribute, boolean domap)
- throws IOException
- {
- ZipFileAttributes zfas = readAttributes();
- if (!domap) {
- try {
- return attribute(AttrID.valueOf(attribute), zfas);
- } catch (IllegalArgumentException x) {}
- return null;
- }
- LinkedHashMap<String, Object> map = new LinkedHashMap<>();
- if ("*".equals(attribute)) {
- for (AttrID id : AttrID.values()) {
- try {
- map.put(id.name(), attribute(id, zfas));
- } catch (IllegalArgumentException x) {}
- }
- } else {
- String[] as = attribute.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;
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributes.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-package com.sun.nio.zipfs;
-
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileTime;
-import java.util.Arrays;
-import java.util.Formatter;
-import static com.sun.nio.zipfs.ZipUtils.*;
-
-/**
- *
- * @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();
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileStore.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-import java.io.IOException;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystems;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileStoreAttributeView;
-import java.nio.file.attribute.FileStoreSpaceAttributeView;
-import java.nio.file.attribute.FileStoreSpaceAttributes;
-import java.nio.file.attribute.Attributes;
-import java.nio.file.attribute.BasicFileAttributeView;
-import java.util.Formatter;
-
-/*
- *
- * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
- */
-
-public class ZipFileStore extends FileStore {
-
- private final ZipFileSystem zfs;
-
- ZipFileStore(ZipPath zpath) {
- this.zfs = (ZipFileSystem)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<? extends FileAttributeView> 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 extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
- if (type == null)
- throw new NullPointerException();
- if (type == FileStoreSpaceAttributeView.class)
- return (V) new ZipFileStoreAttributeView(this);
- return null;
- }
-
- @Override
- public Object getAttribute(String attribute) throws IOException {
- if (attribute.equals("space:totalSpace"))
- return new ZipFileStoreAttributeView(this).readAttributes().totalSpace();
- if (attribute.equals("space:usableSpace"))
- return new ZipFileStoreAttributeView(this).readAttributes().usableSpace();
- if (attribute.equals("space:unallocatedSpace"))
- return new ZipFileStoreAttributeView(this).readAttributes().unallocatedSpace();
- throw new UnsupportedOperationException("does not support the given attribute");
- }
-
- private static class ZipFileStoreAttributeView implements FileStoreSpaceAttributeView {
-
- private final ZipFileStore fileStore;
-
- public ZipFileStoreAttributeView(ZipFileStore fileStore) {
- this.fileStore = fileStore;
- }
-
- @Override
- public String name() {
- return "space";
- }
-
- @Override
- public FileStoreSpaceAttributes readAttributes() throws IOException {
- final String file = fileStore.name();
- Path path = FileSystems.getDefault().getPath(file);
- final long size = Attributes.readBasicFileAttributes(path).size();
- final FileStore fstore = path.getFileStore();
- final FileStoreSpaceAttributes fstoreAttrs =
- Attributes.readFileStoreSpaceAttributes(fstore);
- return new FileStoreSpaceAttributes() {
- public long totalSpace() {
- return size;
- }
-
- public long usableSpace() {
- if (!fstore.isReadOnly())
- return fstoreAttrs.usableSpace();
- return 0;
- }
-
- public long unallocatedSpace() {
- if (!fstore.isReadOnly())
- return fstoreAttrs.unallocatedSpace();
- return 0;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder();
- Formatter fm = new Formatter(sb);
- fm.format("FileStoreSpaceAttributes[%s]%n", file);
- fm.format(" totalSpace: %d%n", totalSpace());
- fm.format(" usableSpace: %d%n", usableSpace());
- fm.format(" unallocSpace: %d%n", unallocatedSpace());
- fm.close();
- return sb.toString();
- }
- };
- }
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystem.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2367 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.*;
-import java.nio.file.*;
-import java.nio.file.attribute.*;
-import java.nio.file.spi.*;
-import java.util.*;
-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.Inflater;
-import java.util.zip.Deflater;
-import java.util.zip.InflaterInputStream;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.ZipException;
-import java.util.zip.ZipError;
-import static java.lang.Boolean.*;
-import static com.sun.nio.zipfs.ZipConstants.*;
-import static com.sun.nio.zipfs.ZipUtils.*;
-import static java.nio.file.StandardOpenOption.*;
-import static java.nio.file.StandardCopyOption.*;
-
-/**
- * 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 buildDirTree; // build a dir tree for directoryStream ops
- 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<String, ?> env)
- throws IOException
- {
- // configurable env setup
- this.buildDirTree = TRUE.equals(env.get("buildDirTreea"));
- this.useTempFile = TRUE.equals(env.get("useTempFile"));
- this.createNew = TRUE.equals(env.get("createNew"));
- this.nameEncoding = env.containsKey("nameEncoding") ?
- (String)env.get("nameEncoding") : "UTF-8";
- 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 (zfpath.notExists()) {
- if (createNew) {
- OutputStream os = zfpath.newOutputStream(CREATE_NEW, WRITE);
- new END().write(os, 0);
- os.close();
- } else {
- throw new FileSystemNotFoundException(zfpath.toString());
- }
- }
- zfpath.checkAccess(AccessMode.READ); // sm and existence check
- try {
- zfpath.checkAccess(AccessMode.WRITE);
- } catch (AccessDeniedException x) {
- this.readOnly = true;
- }
- this.zc = ZipCoder.get(nameEncoding);
- this.defaultdir = new ZipPath(this, getBytes(defaultDir));
- this.ch = zfpath.newByteChannel(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<Path> getRootDirectories() {
- ArrayList<Path> pathArr = new ArrayList<>();
- pathArr.add(new ZipPath(this, new byte[]{'/'}));
- return pathArr;
- }
-
- ZipPath getDefaultDir() { // package private
- return defaultdir;
- }
-
- @Override
- public ZipPath getPath(String path) {
- if (path.length() == 0)
- throw new InvalidPathException(path, "path should not be empty");
- 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<FileStore> getFileStores() {
- ArrayList<FileStore> list = new ArrayList<>(1);
- list.add(new ZipFileStore(new ZipPath(this, new byte[]{'/'})));
- return list;
- }
-
- private static final Set<String> supportedFileAttributeViews =
- Collections.unmodifiableSet(
- new HashSet<String>(Arrays.asList("basic", "zip")));
-
- @Override
- public Set<String> 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<InputStream> 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();
- }
-
- synchronized (tmppaths) {
- for (Path p: tmppaths) {
- try {
- p.deleteIfExists();
- } catch (IOException x) {
- x.printStackTrace();
- }
- }
- }
- provider.removeFileSystem(zfpath);
- }
-
- ZipFileAttributes getFileAttributes(byte[] path)
- throws IOException
- {
- Entry e;
- beginRead();
- try {
- ensureOpen();
- e = getEntry0(path);
- } finally {
- endRead();
- }
- if (e == null) {
- if (path.length == 0) {
- e = new Entry(new byte[0]); // root
- } else if (buildDirTree) {
- IndexNode inode = getDirs().get(IndexNode.keyOf(path));
- if (inode == null)
- return null;
- e = new Entry(inode.name);
- } else {
- return null;
- }
- e.method = METHOD_STORED; // STORED for dir
- BasicFileAttributes bfas = Attributes.readBasicFileAttributes(zfpath);
- if (bfas.lastModifiedTime() != null)
- e.mtime = bfas.lastModifiedTime().toMillis();
- if (bfas.lastAccessTime() != null)
- e.atime = bfas.lastAccessTime().toMillis();
- if (bfas.creationTime() != null)
- e.ctime = bfas.creationTime().toMillis();
- }
- 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 getEntry0(path) != null;
- } finally {
- endRead();
- }
- }
-
- boolean isDirectory(byte[] path)
- throws IOException
- {
- if (buildDirTree)
- return getDirs().containsKey(IndexNode.keyOf(path));
-
- beginRead();
- try {
- Entry e = getEntry0(path);
- return (e != null && e.isDir()) || path.length == 0;
- } 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<Path> iteratorOf(byte[] path,
- DirectoryStream.Filter<? super Path> filter)
- throws IOException
- {
- beginWrite(); // iteration of inodes needs exclusive lock
- try {
- ensureOpen();
- if (buildDirTree) {
- IndexNode inode = getDirs().get(IndexNode.keyOf(path));
- if (inode == null)
- throw new NotDirectoryException(getString(path));
- List<Path> 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();
- }
-
- if (!isDirectory(path))
- throw new NotDirectoryException(getString(path));
- List<Path> list = new ArrayList<>();
- path = toDirectoryPath(path);
- for (IndexNode key : inodes.keySet()) {
- if (!isParentOf(path, key.name)) // is "path" the parent of "name"
- continue;
- int off = path.length;
- while (off < key.name.length) {
- if (key.name[off] == '/')
- break;
- off++;
- }
- if (off < (key.name.length - 1))
- continue;
- ZipPath zp = toZipPath(key.name);
- if (filter == null || filter.accept(zp))
- list.add(zp);
- }
- 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);
- eSrc.file.copyTo(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<? extends OpenOption> 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<? extends OpenOption> 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 {
- return rbc.read(dst);
- }
-
- 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<? extends OpenOption> 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 = Attributes.readBasicFileAttributes(u.file).size();
- 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<InputStream> streams =
- Collections.synchronizedSet(new HashSet<InputStream>());
-
- // the ex-channel and ex-path that need to close when their outstanding
- // input streams are all closed by the obtainers.
- private Set<ExChannelCloser> exChClosers = new HashSet<>();
-
- private Set<Path> tmppaths = Collections.synchronizedSet(new HashSet<Path>());
- private Path getTempPathForEntry(byte[] path) throws IOException {
- Path tmpPath = createTempFileInSameDirectoryAs(zfpath);
- if (path != null) {
- Entry e = getEntry0(path);
- if (e != null) {
- InputStream is = newInputStream(path);
- OutputStream os = tmpPath.newOutputStream(WRITE);
- try {
- copyStream(is, os);
- } finally {
- is.close();
- os.close();
- }
- }
- }
- return tmpPath;
- }
-
- private void removeTempPathForEntry(Path path) throws IOException {
- path.delete();
- 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) {
- if (!inodes.containsKey(IndexNode.keyOf(path)))
- throw new NoSuchFileException(getString(path));
- }
- } finally {
- endRead();
- }
- }
-
- 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 null; // top entry
- return Arrays.copyOf(path, off + 1);
- }
-
- // If "starter" is the parent directory of "path"
- private static boolean isParentOf(byte[] p, byte[] c) {
- final int plen = p.length;
- if (plen == 0) // root dir
- return true;
- if (plen >= c.length)
- return false;
- int n = 0;
- while (n < plen) {
- if (p[n] != c[n])
- return false;
- n++;
- }
- if (p[n - 1] != '/' && (c[n] != '/' || n == c.length - 1))
- return false;
- return true;
- }
-
- 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<IndexNode, IndexNode> 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;
- 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)");
- }
- 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 File.createTempFile.
- private Path createTempFileInSameDirectoryAs(Path path)
- throws IOException
- {
- Path parent = path.toAbsolutePath().getParent();
- String dir = (parent == null)? "." : parent.toString();
- Path tmpPath = File.createTempFile("zipfstmp", null, new File(dir)).toPath();
- tmppaths.add(tmpPath);
- return tmpPath;
- }
-
- ////////////////////update & sync //////////////////////////////////////
-
- private boolean hasUpdate = false;
-
- private void updateDelete(Entry e) {
- beginWrite();
- try {
- inodes.remove(IndexNode.keyOf(e.name)); //inodes.remove(e.name);
- hasUpdate = true;
- dirs = null;
- } finally {
- endWrite();
- }
- }
-
- private void update(Entry e) {
- beginWrite();
- try {
- inodes.put(IndexNode.keyOf(e.name), e); //inodes.put(e, e);
- hasUpdate = true;
- dirs = null;
- } 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;
- size += LOCNAM(buf) + LOCEXT(buf) + LOCSIZ(buf);
- 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();
- ecc.path.delete();
- exChClosers.remove(ecc);
- }
- }
- }
- if (!hasUpdate)
- return;
- Path tmpFile = createTempFileInSameDirectoryAs(zfpath);
- OutputStream os = tmpFile.newOutputStream(WRITE);
- ArrayList<Entry> 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 or FILECH
- 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
- InputStream is = e.file.newInputStream();
- 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
- OutputStream os2 = new EntryOutputStream(e, os);
- while ((n = is.read(buf)) != -1) {
- os2.write(buf, 0, n);
- }
- os2.close();
- written += e.csize;
- if ((e.flag & FLAG_DATADESCR) != 0)
- written += e.writeEXT(os);
- }
- is.close();
- e.file.delete();
- 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
- 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);
- os.close();
-
- 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);
- zfpath.moveTo(ecc.path, REPLACE_EXISTING);
- exChClosers.add(ecc);
- streams = Collections.synchronizedSet(new HashSet<InputStream>());
- } else {
- ch.close();
- zfpath.delete();
- }
-
- tmpFile.moveTo(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 Entry getEntry0(byte[] path) throws IOException {
- if (path == null)
- throw new NullPointerException("path");
- if (path.length == 0)
- return null;
- IndexNode inode = null;
- IndexNode key = IndexNode.keyOf(path);
- if ((inode = inodes.get(key)) == null) {
- if (path[path.length -1] == '/') // already has a slash
- return null;
- path = Arrays.copyOf(path, path.length + 1);
- path[path.length - 1] = '/';
- if ((inode = inodes.get(key.as(path))) == null)
- return null;
- }
- if (inode instanceof Entry)
- return (Entry)inode;
- return Entry.readCEN(this, inode.pos);
- }
-
- // Test if the "name" a parent directory of any entry (dir empty)
- boolean isAncestor(byte[] name) {
- for (Map.Entry<IndexNode, IndexNode> entry : inodes.entrySet()) {
- byte[] ename = entry.getKey().name;
- if (isParentOf(name, ename))
- return true;
- }
- return false;
- }
-
- public void deleteFile(byte[] path, boolean failIfNotExists)
- throws IOException
- {
- checkWritable();
- Entry e = getEntry0(path);
- if (e == 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 (e.isDir() && isAncestor(path))
- throw new DirectoryNotEmptyException(getString(path));
- updateDelete(e);
- }
- }
-
- 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 = e.file.newOutputStream(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 = e.file.newInputStream();
- else
- throw new ZipException("update entry data is missing");
- } else if (e.type == Entry.FILECH) {
- // FILECH result is un-compressed.
- eis = e.file.newInputStream();
- // 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<Inflater> 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 = (Inflater)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<Deflater> 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 = (Deflater)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) {
- as(name);
- this.pos = pos;
- }
-
- final static IndexNode keyOf(byte[] name) { // get a lookup key;
- return new IndexNode(name, -1);
- }
-
- final IndexNode as(byte[] name) { // reuse the node, mostly
- this.name = name; // as a lookup "key"
- this.hashcode = Arrays.hashCode(name);
- return this;
- }
-
- 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) {
- this.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) {
- this.version = e.version;
- this.name = e.name;
- 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;
- }
-
- boolean isDir() {
- return name != null &&
- (name.length == 0 ||
- name[name.length - 1] == '/');
- }
-
- 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); // 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
- writeInt(os, mtime); // last modification time
- 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<InputStream> streams;
- ExChannelCloser(Path path,
- SeekableByteChannel ch,
- Set<InputStream> 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 HashMap<IndexNode, IndexNode> dirs;
- private IndexNode root;
- private IndexNode addToDir(IndexNode child) {
- IndexNode cinode = dirs.get(child);
- if (cinode != null)
- return cinode;
-
- byte[] cname = child.name;
- byte[] pname = getParent(cname);
- IndexNode pinode;
-
- if (pname != null)
- pinode = addToDir(IndexNode.keyOf(pname));
- else
- pinode = root;
- cinode = inodes.get(child);
- if (cname[cname.length -1] != '/') { // not a dir
- cinode.sibling = pinode.child;
- pinode.child = cinode;
- return null;
- }
- //cinode = dirs.get(child);
- if (cinode == null) // pseudo directry entry
- cinode = new IndexNode(cname, -1);
- cinode.sibling = pinode.child;
- pinode.child = cinode;
-
- dirs.put(child, cinode);
- return cinode;
- }
-
- private HashMap<IndexNode, IndexNode> getDirs()
- throws IOException
- {
- beginWrite();
- try {
- if (dirs != null)
- return dirs;
- dirs = new HashMap<>();
- root = new IndexNode(new byte[0], -1);
- dirs.put(root, root);
- for (IndexNode node : inodes.keySet())
- addToDir(node);
- return dirs;
- } finally {
- endWrite();
- }
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystemProvider.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-import java.io.IOException;
-import java.nio.channels.FileChannel;
-import java.nio.file.FileRef;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.FileSystemAlreadyExistsException;
-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.FileAttribute;
-import java.nio.file.spi.FileSystemProvider;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/*
- *
- * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
- */
-
-public class ZipFileSystemProvider extends FileSystemProvider {
-
-
- private final Map<Path, ZipFileSystem> filesystems = new HashMap<>();
-
- public ZipFileSystemProvider() {}
-
- @Override
- public String getScheme() {
- return "zip";
- }
-
- protected Path uriToPath(URI uri) {
- String scheme = uri.getScheme();
- if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
- throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
- }
- try {
- return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null))
- .toAbsolutePath();
- } catch (URISyntaxException e) {
- throw new AssertionError(e); //never thrown
- }
- }
-
- @Override
- public FileSystem newFileSystem(URI uri, Map<String, ?> env)
- throws IOException
- {
- return newFileSystem(uriToPath(uri), env);
- }
-
- @Override
- public FileSystem newFileSystem(FileRef file, Map<String, ?> env)
- throws IOException
- {
- if (!(file instanceof Path))
- throw new UnsupportedOperationException();
- Path path = (Path)file;
- if (!path.toUri().getScheme().equalsIgnoreCase("file")) {
- throw new UnsupportedOperationException();
- }
- return newFileSystem(path, env);
- }
-
- private FileSystem newFileSystem(Path path, Map<String, ?> env)
- throws IOException
- {
- synchronized(filesystems) {
- Path realPath = null;
- if (path.exists()) {
- realPath = path.toRealPath(true);
- if (filesystems.containsKey(realPath))
- throw new FileSystemAlreadyExistsException();
- }
- ZipFileSystem zipfs = new ZipFileSystem(this, path, env);
- if (realPath == null)
- realPath = path.toRealPath(true);
- filesystems.put(realPath, zipfs);
- return zipfs;
- }
- }
-
- @Override
- public Path getPath(URI uri) {
- FileSystem fs = getFileSystem(uri);
- String fragment = uri.getFragment();
- if (fragment == null) {
- throw new IllegalArgumentException("URI: "
- + uri
- + " does not contain path fragment ex. zip:///c:/foo.zip#/BAR");
- }
- return fs.getPath(fragment);
- }
-
- @Override
- public FileChannel newFileChannel(Path path,
- Set<? extends OpenOption> options,
- FileAttribute<?>... attrs)
- throws IOException
- {
- if (path == null)
- throw new NullPointerException("path is null");
- if (path instanceof ZipPath)
- return ((ZipPath)path).newFileChannel(options, attrs);
- throw new ProviderMismatchException();
- }
-
- @Override
- public FileSystem getFileSystem(URI uri) {
- synchronized (filesystems) {
- ZipFileSystem zipfs = null;
- try {
- zipfs = filesystems.get(uriToPath(uri).toRealPath(true));
- } catch (IOException x) {
- // ignore the ioe from toRealPath(), return FSNFE
- }
- if (zipfs == null)
- throw new FileSystemNotFoundException();
- return zipfs;
- }
- }
-
- void removeFileSystem(Path zfpath) throws IOException {
- synchronized (filesystems) {
- filesystems.remove(zfpath.toRealPath(true));
- }
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipInfo.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-import com.sun.nio.zipfs.ZipFileSystem.Entry;
-import static com.sun.nio.zipfs.ZipConstants.*;
-import static com.sun.nio.zipfs.ZipUtils.*;
-
-/**
- * Print all loc and cen headers of the ZIP file
- *
- * @author Xueming Shen
- */
-
-public class ZipInfo {
-
- public static void main(String[] args) throws Throwable {
- if (args.length < 1) {
- print("Usage: java ZipInfo zfname");
- } else {
- Map<String, ?> env = Collections.emptyMap();
- ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider()
- .newFileSystem(Paths.get(args[0]), env));
- byte[] cen = zfs.cen;
- if (cen == null) {
- print("zip file is empty%n");
- return;
- }
- int pos = 0;
- byte[] buf = new byte[1024];
- int no = 1;
- while (pos + CENHDR < cen.length) {
- print("----------------#%d--------------------%n", no++);
- printCEN(cen, pos);
-
- // use size CENHDR as the extra bytes to read, just in case the
- // loc.extra is bigger than the cen.extra, try to avoid to read
- // twice
- long len = LOCHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENHDR;
- if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
- zfs.zerror("read loc header failed");
- if (LOCEXT(buf) > CENEXT(cen, pos) + CENHDR) {
- // have to read the second time;
- len = LOCHDR + LOCNAM(buf) + LOCEXT(buf);
- if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
- zfs.zerror("read loc header failed");
- }
- printLOC(buf);
- pos += CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos);
- }
- zfs.close();
- }
- }
-
- static void print(String fmt, Object... objs) {
- System.out.printf(fmt, objs);
- }
-
- static void printLOC(byte[] loc) {
- print("%n");
- print("[Local File Header]%n");
- print(" Signature : %#010x%n", LOCSIG(loc));
- if (LOCSIG(loc) != LOCSIG) {
- print(" Wrong signature!");
- return;
- }
- print(" Version : %#6x [%d.%d]%n",
- LOCVER(loc), LOCVER(loc) / 10, LOCVER(loc) % 10);
- print(" Flag : %#6x%n", LOCFLG(loc));
- print(" Method : %#6x%n", LOCHOW(loc));
- print(" LastMTime : %#10x [%tc]%n",
- LOCTIM(loc), dosToJavaTime(LOCTIM(loc)));
- print(" CRC : %#10x%n", LOCCRC(loc));
- print(" CSize : %#10x%n", LOCSIZ(loc));
- print(" Size : %#10x%n", LOCLEN(loc));
- print(" NameLength : %#6x [%s]%n",
- LOCNAM(loc), new String(loc, LOCHDR, LOCNAM(loc)));
- print(" ExtraLength : %#6x%n", LOCEXT(loc));
- if (LOCEXT(loc) != 0)
- printExtra(loc, LOCHDR + LOCNAM(loc), LOCEXT(loc));
- }
-
- static void printCEN(byte[] cen, int off) {
- print("[Central Directory Header]%n");
- print(" Signature : %#010x%n", CENSIG(cen, off));
- if (CENSIG(cen, off) != CENSIG) {
- print(" Wrong signature!");
- return;
- }
- print(" VerMadeby : %#6x [%d, %d.%d]%n",
- CENVEM(cen, off), (CENVEM(cen, off) >> 8),
- (CENVEM(cen, off) & 0xff) / 10,
- (CENVEM(cen, off) & 0xff) % 10);
- print(" VerExtract : %#6x [%d.%d]%n",
- CENVER(cen, off), CENVER(cen, off) / 10, CENVER(cen, off) % 10);
- print(" Flag : %#6x%n", CENFLG(cen, off));
- print(" Method : %#6x%n", CENHOW(cen, off));
- print(" LastMTime : %#10x [%tc]%n",
- CENTIM(cen, off), dosToJavaTime(CENTIM(cen, off)));
- print(" CRC : %#10x%n", CENCRC(cen, off));
- print(" CSize : %#10x%n", CENSIZ(cen, off));
- print(" Size : %#10x%n", CENLEN(cen, off));
- print(" NameLen : %#6x [%s]%n",
- CENNAM(cen, off), new String(cen, off + CENHDR, CENNAM(cen, off)));
- print(" ExtraLen : %#6x%n", CENEXT(cen, off));
- if (CENEXT(cen, off) != 0)
- printExtra(cen, off + CENHDR + CENNAM(cen, off), CENEXT(cen, off));
- print(" CommentLen : %#6x%n", CENCOM(cen, off));
- print(" DiskStart : %#6x%n", CENDSK(cen, off));
- print(" Attrs : %#6x%n", CENATT(cen, off));
- print(" AttrsEx : %#10x%n", CENATX(cen, off));
- print(" LocOff : %#10x%n", CENOFF(cen, off));
-
- }
-
- static long locoff(byte[] cen, int pos) {
- long locoff = CENOFF(cen, pos);
- if (locoff == ZIP64_MINVAL) { //ZIP64
- int off = pos + CENHDR + CENNAM(cen, pos);
- int end = off + CENEXT(cen, pos);
- while (off + 4 < end) {
- int tag = SH(cen, off);
- int sz = SH(cen, off + 2);
- if (tag != EXTID_ZIP64) {
- off += 4 + sz;
- continue;
- }
- off += 4;
- if (CENLEN(cen, pos) == ZIP64_MINVAL)
- off += 8;
- if (CENSIZ(cen, pos) == ZIP64_MINVAL)
- off += 8;
- return LL(cen, off);
- }
- // should never be here
- }
- return locoff;
- }
-
- static void printExtra(byte[] extra, int off, int len) {
- int end = off + len;
- while (off + 4 < end) {
- int tag = SH(extra, off);
- int sz = SH(extra, off + 2);
- print(" [tag=0x%04x, sz=%d, data= ", tag, sz);
- if (off + sz > end) {
- print(" Error: Invalid extra data, beyond extra length");
- break;
- }
- off += 4;
- for (int i = 0; i < sz; i++)
- print("%02x ", extra[off + i]);
- print("]%n");
- switch (tag) {
- case EXTID_ZIP64 :
- print(" ->ZIP64: ");
- int pos = off;
- while (pos + 8 <= off + sz) {
- print(" *0x%x ", LL(extra, pos));
- pos += 8;
- }
- print("%n");
- break;
- case EXTID_NTFS:
- print(" ->PKWare NTFS%n");
- // 4 bytes reserved
- if (SH(extra, off + 4) != 0x0001 || SH(extra, off + 6) != 24)
- print(" Error: Invalid NTFS sub-tag or subsz");
- print(" mtime:%tc%n",
- winToJavaTime(LL(extra, off + 8)));
- print(" atime:%tc%n",
- winToJavaTime(LL(extra, off + 16)));
- print(" ctime:%tc%n",
- winToJavaTime(LL(extra, off + 24)));
- break;
- case EXTID_EXTT:
- print(" ->Inof-ZIP Extended Timestamp: flag=%x%n",extra[off]);
- pos = off + 1 ;
- while (pos + 4 <= off + sz) {
- print(" *%tc%n",
- unixToJavaTime(LG(extra, pos)));
- pos += 4;
- }
- break;
- default:
- }
- off += sz;
- }
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipPath.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,961 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-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.*;
-import java.nio.file.DirectoryStream.Filter;
-import java.nio.file.attribute.BasicFileAttributeView;
-import java.nio.file.attribute.FileAttribute;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileTime;
-import java.util.*;
-import static java.nio.file.StandardOpenOption.*;
-import static java.nio.file.StandardCopyOption.*;
-
-/**
- *
- * @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
- */
-
-public class ZipPath extends 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 getName() {
- 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(boolean resolveLinks) throws IOException {
- ZipPath realPath = new ZipPath(zfs, getResolvedPath());
- realPath.checkAccess();
- return realPath;
- }
-
- @Override
- public 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() {
- String zfPath = zfs.toString();
- if (File.separatorChar == '\\') // replace all separators by '/'
- zfPath = "/" + zfPath.replace("\\", "/");
- try {
- return new URI("zip", "",
- zfPath,
- zfs.getString(toAbsolutePath().path));
- } 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 null;
- 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[0] == '/');
- }
-
- @Override
- public ZipPath resolve(Path other) {
- if (other == null)
- return this;
- 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 ZipPath resolve(String other) {
- return resolve(getFileSystem().getPath(other));
- }
-
- @Override
- public boolean startsWith(Path other) {
- final ZipPath o = checkPath(other);
- if (o.isAbsolute() != this.isAbsolute())
- return false;
- final int oCount = o.getNameCount();
- if (getNameCount() < oCount)
- return false;
- for (int i = 0; i < oCount; i++) {
- if (!o.getName(i).equals(getName(i)))
- return false;
- }
- return true;
- }
-
- @Override
- public boolean endsWith(Path other) {
- final ZipPath o = checkPath(other);
- if (o.isAbsolute())
- return this.isAbsolute() ? this.equals(o) : false;
- int i = o.getNameCount();
- int j = this.getNameCount();
- if (j < i)
- return false;
- for (--i, --j; i >= 0; i--, j--) {
- if (!o.getName(i).equals(this.getName(j)))
- return false;
- }
- return true;
- }
-
- @Override
- public Path normalize() {
- byte[] resolved = getResolved();
- if (resolved == path) // no change
- return this;
- if (resolved.length == 0)
- return null;
- 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)'.')
- 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;
- }
-
- @Override
- public Path createSymbolicLink(
- Path target, FileAttribute<?>... attrs) throws IOException {
- throw new UnsupportedOperationException("Not supported.");
- }
-
- @Override
- public Path createLink(
- Path existing) throws IOException {
- throw new UnsupportedOperationException("Not supported.");
- }
-
- @Override
- public Path readSymbolicLink() throws IOException {
- throw new UnsupportedOperationException("Not supported.");
- }
-
- @Override
- public Path createDirectory(FileAttribute<?>... attrs)
- throws IOException
- {
- zfs.createDirectory(getResolvedPath(), attrs);
- return this;
- }
-
- public final Path createFile(FileAttribute<?>... attrs)
- throws IOException
- {
- OutputStream os = newOutputStream(CREATE_NEW, WRITE);
- try {
- os.close();
- } catch (IOException x) {}
- return this;
- }
-
- @Override
- public 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());
- }
-
- private static final DirectoryStream.Filter<Path> acceptAllFilter =
- new DirectoryStream.Filter<>() {
- @Override public boolean accept(Path entry) { return true; }
- };
-
- @Override
- public final DirectoryStream<Path> newDirectoryStream() throws IOException {
- return newDirectoryStream(acceptAllFilter);
- }
-
- @Override
- public DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
- throws IOException
- {
- return new ZipDirectoryStream(this, filter);
- }
-
- @Override
- public final DirectoryStream<Path> newDirectoryStream(String glob)
- throws IOException
- {
- // avoid creating a matcher if all entries are required.
- if (glob.equals("*"))
- return newDirectoryStream();
-
- // create a matcher and return a filter that uses it.
- final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob);
- DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<>() {
- @Override
- public boolean accept(Path entry) {
- return matcher.matches(entry.getName());
- }
- };
- return newDirectoryStream(filter);
- }
-
- @Override
- public final void delete() throws IOException {
- zfs.deleteFile(getResolvedPath(), true);
- }
-
- @Override
- public final 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;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public <V extends FileAttributeView> V getFileAttributeView(Class<V> type,
- LinkOption... options)
- {
- return (V)ZipFileAttributeView.get(this, type);
- }
-
- @Override
- public 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);
- }
-
- private Object getAttributesImpl(String attribute, boolean domap)
- throws IOException
- {
- String view = null;
- String attr = null;
- int colonPos = attribute.indexOf(':');
- if (colonPos == -1) {
- view = "basic";
- attr = attribute;
- } else {
- view = attribute.substring(0, colonPos++);
- attr = attribute.substring(colonPos);
- }
- ZipFileAttributeView zfv = ZipFileAttributeView.get(this, view);
- if (zfv == null) {
- throw new UnsupportedOperationException("view not supported");
- }
- return zfv.getAttribute(attr, domap);
- }
-
- @Override
- public Object getAttribute(String attribute, LinkOption... options)
- throws IOException
- {
- return getAttributesImpl(attribute, false);
- }
-
- @Override
- public Map<String,?> readAttributes(String attribute, LinkOption... options)
- throws IOException
- {
- return (Map<String, ?>)getAttributesImpl(attribute, true);
- }
-
- @Override
- public 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));
- }
-
- @Override
- public boolean isSameFile(Path other) throws IOException {
- if (other == null ||
- this.getFileSystem() != other.getFileSystem())
- return false;
- this.checkAccess();
- other.checkAccess();
- return Arrays.equals(this.getResolvedPath(),
- ((ZipPath)other).getResolvedPath());
- }
-
- 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 Iterator<Path> 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();
- }
- };
- }
-
- @Override
- public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
- FileAttribute<?>... attrs)
- throws IOException
- {
- return zfs.newByteChannel(getResolvedPath(), options, attrs);
- }
-
-
- FileChannel newFileChannel(Set<? extends OpenOption> options,
- FileAttribute<?>... attrs)
- throws IOException
- {
- return zfs.newFileChannel(getResolvedPath(), options, attrs);
- }
-
- @Override
- public SeekableByteChannel newByteChannel(OpenOption... options)
- throws IOException {
- Set<OpenOption> set = new HashSet<>(options.length);
- Collections.addAll(set, options);
- return newByteChannel(set);
- }
-
- @Override
- public 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());
-
- }
-
- @Override
- public boolean exists() {
- if (path.length == 1 && path[0] == '/')
- return true;
- try {
- return zfs.exists(getResolvedPath());
- } catch (IOException x) {}
- return false;
- }
-
- @Override
- public boolean notExists() {
- return !exists();
- }
-
-
- @Override
- public OutputStream newOutputStream(OpenOption... options)
- throws IOException
- {
- if (options.length == 0)
- return zfs.newOutputStream(getResolvedPath(),
- CREATE_NEW, WRITE);
- return zfs.newOutputStream(getResolvedPath(), options);
- }
-
- @Override
- public Path moveTo(Path target, CopyOption... options)
- throws IOException
- {
- if (this.zfs.provider() == target.getFileSystem().provider() &&
- this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile()))
- {
- zfs.copyFile(true,
- getResolvedPath(),
- ((ZipPath)target).getResolvedPath(),
- options);
- } else {
- copyToTarget(target, options);
- delete();
- }
- return target;
- }
-
- @Override
- public Path copyTo(Path target, CopyOption... options)
- throws IOException
- {
- if (this.zfs.provider() == target.getFileSystem().provider() &&
- this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile()))
- {
- zfs.copyFile(false,
- getResolvedPath(),
- ((ZipPath)target).getResolvedPath(),
- options);
- } else {
- copyToTarget(target, options);
- }
- return target;
- }
-
- private void copyToTarget(Path 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 =
- target.getFileAttributeView(BasicFileAttributeView.class);
- try {
- view.setTimes(zfas.lastModifiedTime(),
- zfas.lastAccessTime(),
- zfas.creationTime());
- } catch (IOException x) {
- // rollback?
- try {
- target.delete();
- } catch (IOException ignore) { }
- throw x;
- }
- }
- }
-}
--- a/jdk/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipUtils.java Mon Dec 06 18:52:23 2010 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +0,0 @@
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of Oracle nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.sun.nio.zipfs;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.regex.PatternSyntaxException;
-import java.util.concurrent.TimeUnit;
-
-/**
- *
- * @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();
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/META-INF/services/java.nio.file.spi.FileSystemProvider Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,2 @@
+com.sun.nio.zipfs.ZipFileSystemProvider
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/JarFileSystemProvider.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Sun Microsystems nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.sun.nio.zipfs;
+
+import java.nio.file.*;
+import java.nio.file.spi.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.FileSystemProvider;
+
+import java.net.URI;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.channels.FileChannel;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class JarFileSystemProvider extends ZipFileSystemProvider
+{
+
+ @Override
+ public String getScheme() {
+ return "jar";
+ }
+
+ @Override
+ protected Path uriToPath(URI uri) {
+ String scheme = uri.getScheme();
+ if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
+ throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
+ }
+ try {
+ String uristr = uri.toString();
+ int end = uristr.indexOf("!/");
+ uristr = uristr.substring(4, (end == -1) ? uristr.length() : end);
+ uri = new URI(uristr);
+ return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null))
+ .toAbsolutePath();
+ } catch (URISyntaxException e) {
+ throw new AssertionError(e); //never thrown
+ }
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ FileSystem fs = getFileSystem(uri);
+ String path = uri.getFragment();
+ if (path == null) {
+ String uristr = uri.toString();
+ int off = uristr.indexOf("!/");
+ if (off != -1)
+ path = uristr.substring(off + 2);
+ }
+ if (path != null)
+ return fs.getPath(path);
+ throw new IllegalArgumentException("URI: "
+ + uri
+ + " does not contain path fragment ex. jar:///c:/foo.zip!/BAR");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipCoder.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.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<CharsetDecoder> decTL = new ThreadLocal<>();
+ private final ThreadLocal<CharsetEncoder> 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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipConstants.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.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 b[n] & 0xff;
+ }
+
+ static final int SH(byte[] b, int n) {
+ return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8);
+ }
+
+ 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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipDirectoryStream.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+import java.nio.file.DirectoryStream;
+import java.nio.file.ClosedDirectoryStreamException;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.io.IOException;
+
+/**
+ *
+ * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
+ */
+
+public class ZipDirectoryStream implements DirectoryStream<Path> {
+
+ private final ZipFileSystem zipfs;
+ private final byte[] path;
+ private final DirectoryStream.Filter<? super Path> filter;
+ private volatile boolean isClosed;
+ private volatile Iterator<Path> itr;
+
+ ZipDirectoryStream(ZipPath zipPath,
+ DirectoryStream.Filter<? super java.nio.file.Path> 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<Path> 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;
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileAttributeView.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package com.sun.nio.zipfs;
+
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+
+/*
+ * @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 extends FileAttributeView> V get(ZipPath path, Class<V> 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");
+ }
+
+ public Object getAttribute(String attribute, boolean domap)
+ throws IOException
+ {
+ ZipFileAttributes zfas = readAttributes();
+ if (!domap) {
+ try {
+ return attribute(AttrID.valueOf(attribute), zfas);
+ } catch (IllegalArgumentException x) {}
+ return null;
+ }
+ LinkedHashMap<String, Object> map = new LinkedHashMap<>();
+ if ("*".equals(attribute)) {
+ for (AttrID id : AttrID.values()) {
+ try {
+ map.put(id.name(), attribute(id, zfas));
+ } catch (IllegalArgumentException x) {}
+ }
+ } else {
+ String[] as = attribute.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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileAttributes.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package com.sun.nio.zipfs;
+
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.util.Arrays;
+import java.util.Formatter;
+import static com.sun.nio.zipfs.ZipUtils.*;
+
+/**
+ *
+ * @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();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileStore.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+import java.io.IOException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileStoreAttributeView;
+import java.nio.file.attribute.FileStoreSpaceAttributeView;
+import java.nio.file.attribute.FileStoreSpaceAttributes;
+import java.nio.file.attribute.Attributes;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.util.Formatter;
+
+/*
+ *
+ * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
+ */
+
+public class ZipFileStore extends FileStore {
+
+ private final ZipFileSystem zfs;
+
+ ZipFileStore(ZipPath zpath) {
+ this.zfs = (ZipFileSystem)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<? extends FileAttributeView> 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 extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
+ if (type == null)
+ throw new NullPointerException();
+ if (type == FileStoreSpaceAttributeView.class)
+ return (V) new ZipFileStoreAttributeView(this);
+ return null;
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException {
+ if (attribute.equals("space:totalSpace"))
+ return new ZipFileStoreAttributeView(this).readAttributes().totalSpace();
+ if (attribute.equals("space:usableSpace"))
+ return new ZipFileStoreAttributeView(this).readAttributes().usableSpace();
+ if (attribute.equals("space:unallocatedSpace"))
+ return new ZipFileStoreAttributeView(this).readAttributes().unallocatedSpace();
+ throw new UnsupportedOperationException("does not support the given attribute");
+ }
+
+ private static class ZipFileStoreAttributeView implements FileStoreSpaceAttributeView {
+
+ private final ZipFileStore fileStore;
+
+ public ZipFileStoreAttributeView(ZipFileStore fileStore) {
+ this.fileStore = fileStore;
+ }
+
+ @Override
+ public String name() {
+ return "space";
+ }
+
+ @Override
+ public FileStoreSpaceAttributes readAttributes() throws IOException {
+ final String file = fileStore.name();
+ Path path = FileSystems.getDefault().getPath(file);
+ final long size = Attributes.readBasicFileAttributes(path).size();
+ final FileStore fstore = path.getFileStore();
+ final FileStoreSpaceAttributes fstoreAttrs =
+ Attributes.readFileStoreSpaceAttributes(fstore);
+ return new FileStoreSpaceAttributes() {
+ public long totalSpace() {
+ return size;
+ }
+
+ public long usableSpace() {
+ if (!fstore.isReadOnly())
+ return fstoreAttrs.usableSpace();
+ return 0;
+ }
+
+ public long unallocatedSpace() {
+ if (!fstore.isReadOnly())
+ return fstoreAttrs.unallocatedSpace();
+ return 0;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Formatter fm = new Formatter(sb);
+ fm.format("FileStoreSpaceAttributes[%s]%n", file);
+ fm.format(" totalSpace: %d%n", totalSpace());
+ fm.format(" usableSpace: %d%n", usableSpace());
+ fm.format(" unallocSpace: %d%n", unallocatedSpace());
+ fm.close();
+ return sb.toString();
+ }
+ };
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystem.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,2369 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.nio.file.spi.*;
+import java.util.*;
+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.Inflater;
+import java.util.zip.Deflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.ZipException;
+import java.util.zip.ZipError;
+import static java.lang.Boolean.*;
+import static com.sun.nio.zipfs.ZipConstants.*;
+import static com.sun.nio.zipfs.ZipUtils.*;
+import static java.nio.file.StandardOpenOption.*;
+import static java.nio.file.StandardCopyOption.*;
+
+/**
+ * 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 buildDirTree; // build a dir tree for directoryStream ops
+ 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<String, ?> 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.buildDirTree = TRUE.equals(env.get("buildDirTreea"));
+ 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 (zfpath.notExists()) {
+ if (createNew) {
+ OutputStream os = zfpath.newOutputStream(CREATE_NEW, WRITE);
+ new END().write(os, 0);
+ os.close();
+ } else {
+ throw new FileSystemNotFoundException(zfpath.toString());
+ }
+ }
+ zfpath.checkAccess(AccessMode.READ); // sm and existence check
+ try {
+ zfpath.checkAccess(AccessMode.WRITE);
+ } catch (AccessDeniedException x) {
+ this.readOnly = true;
+ }
+ this.zc = ZipCoder.get(nameEncoding);
+ this.defaultdir = new ZipPath(this, getBytes(defaultDir));
+ this.ch = zfpath.newByteChannel(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<Path> getRootDirectories() {
+ ArrayList<Path> pathArr = new ArrayList<>();
+ pathArr.add(new ZipPath(this, new byte[]{'/'}));
+ return pathArr;
+ }
+
+ ZipPath getDefaultDir() { // package private
+ return defaultdir;
+ }
+
+ @Override
+ public ZipPath getPath(String path) {
+ if (path.length() == 0)
+ throw new InvalidPathException(path, "path should not be empty");
+ 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<FileStore> getFileStores() {
+ ArrayList<FileStore> list = new ArrayList<>(1);
+ list.add(new ZipFileStore(new ZipPath(this, new byte[]{'/'})));
+ return list;
+ }
+
+ private static final Set<String> supportedFileAttributeViews =
+ Collections.unmodifiableSet(
+ new HashSet<String>(Arrays.asList("basic", "zip")));
+
+ @Override
+ public Set<String> 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<InputStream> 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();
+ }
+
+ synchronized (tmppaths) {
+ for (Path p: tmppaths) {
+ try {
+ p.deleteIfExists();
+ } catch (IOException x) {
+ x.printStackTrace();
+ }
+ }
+ }
+ provider.removeFileSystem(zfpath);
+ }
+
+ ZipFileAttributes getFileAttributes(byte[] path)
+ throws IOException
+ {
+ Entry e;
+ beginRead();
+ try {
+ ensureOpen();
+ e = getEntry0(path);
+ } finally {
+ endRead();
+ }
+ if (e == null) {
+ if (path.length == 0) {
+ e = new Entry(new byte[0]); // root
+ } else if (buildDirTree) {
+ IndexNode inode = getDirs().get(IndexNode.keyOf(path));
+ if (inode == null)
+ return null;
+ e = new Entry(inode.name);
+ } else {
+ return null;
+ }
+ e.method = METHOD_STORED; // STORED for dir
+ BasicFileAttributes bfas = Attributes.readBasicFileAttributes(zfpath);
+ if (bfas.lastModifiedTime() != null)
+ e.mtime = bfas.lastModifiedTime().toMillis();
+ if (bfas.lastAccessTime() != null)
+ e.atime = bfas.lastAccessTime().toMillis();
+ if (bfas.creationTime() != null)
+ e.ctime = bfas.creationTime().toMillis();
+ }
+ 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 getEntry0(path) != null;
+ } finally {
+ endRead();
+ }
+ }
+
+ boolean isDirectory(byte[] path)
+ throws IOException
+ {
+ if (buildDirTree)
+ return getDirs().containsKey(IndexNode.keyOf(path));
+
+ beginRead();
+ try {
+ Entry e = getEntry0(path);
+ return (e != null && e.isDir()) || path.length == 0;
+ } 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<Path> iteratorOf(byte[] path,
+ DirectoryStream.Filter<? super Path> filter)
+ throws IOException
+ {
+ beginWrite(); // iteration of inodes needs exclusive lock
+ try {
+ ensureOpen();
+ if (buildDirTree) {
+ IndexNode inode = getDirs().get(IndexNode.keyOf(path));
+ if (inode == null)
+ throw new NotDirectoryException(getString(path));
+ List<Path> 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();
+ }
+
+ if (!isDirectory(path))
+ throw new NotDirectoryException(getString(path));
+ List<Path> list = new ArrayList<>();
+ path = toDirectoryPath(path);
+ for (IndexNode key : inodes.keySet()) {
+ if (!isParentOf(path, key.name)) // is "path" the parent of "name"
+ continue;
+ int off = path.length;
+ while (off < key.name.length) {
+ if (key.name[off] == '/')
+ break;
+ off++;
+ }
+ if (off < (key.name.length - 1))
+ continue;
+ ZipPath zp = toZipPath(key.name);
+ if (filter == null || filter.accept(zp))
+ list.add(zp);
+ }
+ 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);
+ eSrc.file.copyTo(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<? extends OpenOption> 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<? extends OpenOption> 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 {
+ return rbc.read(dst);
+ }
+
+ 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<? extends OpenOption> 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 = Attributes.readBasicFileAttributes(u.file).size();
+ 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<InputStream> streams =
+ Collections.synchronizedSet(new HashSet<InputStream>());
+
+ // the ex-channel and ex-path that need to close when their outstanding
+ // input streams are all closed by the obtainers.
+ private Set<ExChannelCloser> exChClosers = new HashSet<>();
+
+ private Set<Path> tmppaths = Collections.synchronizedSet(new HashSet<Path>());
+ private Path getTempPathForEntry(byte[] path) throws IOException {
+ Path tmpPath = createTempFileInSameDirectoryAs(zfpath);
+ if (path != null) {
+ Entry e = getEntry0(path);
+ if (e != null) {
+ InputStream is = newInputStream(path);
+ OutputStream os = tmpPath.newOutputStream(WRITE);
+ try {
+ copyStream(is, os);
+ } finally {
+ is.close();
+ os.close();
+ }
+ }
+ }
+ return tmpPath;
+ }
+
+ private void removeTempPathForEntry(Path path) throws IOException {
+ path.delete();
+ 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) {
+ if (!inodes.containsKey(IndexNode.keyOf(path)))
+ throw new NoSuchFileException(getString(path));
+ }
+ } finally {
+ endRead();
+ }
+ }
+
+ 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 null; // top entry
+ return Arrays.copyOf(path, off + 1);
+ }
+
+ // If "starter" is the parent directory of "path"
+ private static boolean isParentOf(byte[] p, byte[] c) {
+ final int plen = p.length;
+ if (plen == 0) // root dir
+ return true;
+ if (plen >= c.length)
+ return false;
+ int n = 0;
+ while (n < plen) {
+ if (p[n] != c[n])
+ return false;
+ n++;
+ }
+ if (p[n - 1] != '/' && (c[n] != '/' || n == c.length - 1))
+ return false;
+ return true;
+ }
+
+ 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<IndexNode, IndexNode> 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;
+ 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)");
+ }
+ 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 File.createTempFile.
+ private Path createTempFileInSameDirectoryAs(Path path)
+ throws IOException
+ {
+ Path parent = path.toAbsolutePath().getParent();
+ String dir = (parent == null)? "." : parent.toString();
+ Path tmpPath = File.createTempFile("zipfstmp", null, new File(dir)).toPath();
+ tmppaths.add(tmpPath);
+ return tmpPath;
+ }
+
+ ////////////////////update & sync //////////////////////////////////////
+
+ private boolean hasUpdate = false;
+
+ private void updateDelete(Entry e) {
+ beginWrite();
+ try {
+ inodes.remove(IndexNode.keyOf(e.name)); //inodes.remove(e.name);
+ hasUpdate = true;
+ dirs = null;
+ } finally {
+ endWrite();
+ }
+ }
+
+ private void update(Entry e) {
+ beginWrite();
+ try {
+ inodes.put(IndexNode.keyOf(e.name), e); //inodes.put(e, e);
+ hasUpdate = true;
+ dirs = null;
+ } 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();
+ ecc.path.delete();
+ exChClosers.remove(ecc);
+ }
+ }
+ }
+ if (!hasUpdate)
+ return;
+ Path tmpFile = createTempFileInSameDirectoryAs(zfpath);
+ OutputStream os = tmpFile.newOutputStream(WRITE);
+ ArrayList<Entry> 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 or FILECH
+ 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
+ InputStream is = e.file.newInputStream();
+ 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
+ OutputStream os2 = new EntryOutputStream(e, os);
+ while ((n = is.read(buf)) != -1) {
+ os2.write(buf, 0, n);
+ }
+ os2.close();
+ written += e.csize;
+ if ((e.flag & FLAG_DATADESCR) != 0)
+ written += e.writeEXT(os);
+ }
+ is.close();
+ e.file.delete();
+ 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
+ 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);
+ os.close();
+
+ 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);
+ zfpath.moveTo(ecc.path, REPLACE_EXISTING);
+ exChClosers.add(ecc);
+ streams = Collections.synchronizedSet(new HashSet<InputStream>());
+ } else {
+ ch.close();
+ zfpath.delete();
+ }
+
+ tmpFile.moveTo(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 Entry getEntry0(byte[] path) throws IOException {
+ if (path == null)
+ throw new NullPointerException("path");
+ if (path.length == 0)
+ return null;
+ IndexNode inode = null;
+ IndexNode key = IndexNode.keyOf(path);
+ if ((inode = inodes.get(key)) == null) {
+ if (path[path.length -1] == '/') // already has a slash
+ return null;
+ path = Arrays.copyOf(path, path.length + 1);
+ path[path.length - 1] = '/';
+ if ((inode = inodes.get(key.as(path))) == null)
+ return null;
+ }
+ if (inode instanceof Entry)
+ return (Entry)inode;
+ return Entry.readCEN(this, inode.pos);
+ }
+
+ // Test if the "name" a parent directory of any entry (dir empty)
+ boolean isAncestor(byte[] name) {
+ for (Map.Entry<IndexNode, IndexNode> entry : inodes.entrySet()) {
+ byte[] ename = entry.getKey().name;
+ if (isParentOf(name, ename))
+ return true;
+ }
+ return false;
+ }
+
+ public void deleteFile(byte[] path, boolean failIfNotExists)
+ throws IOException
+ {
+ checkWritable();
+ Entry e = getEntry0(path);
+ if (e == 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 (e.isDir() && isAncestor(path))
+ throw new DirectoryNotEmptyException(getString(path));
+ updateDelete(e);
+ }
+ }
+
+ 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 = e.file.newOutputStream(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 = e.file.newInputStream();
+ else
+ throw new ZipException("update entry data is missing");
+ } else if (e.type == Entry.FILECH) {
+ // FILECH result is un-compressed.
+ eis = e.file.newInputStream();
+ // 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<Inflater> 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 = (Inflater)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<Deflater> 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 = (Deflater)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) {
+ as(name);
+ this.pos = pos;
+ }
+
+ final static IndexNode keyOf(byte[] name) { // get a lookup key;
+ return new IndexNode(name, -1);
+ }
+
+ final IndexNode as(byte[] name) { // reuse the node, mostly
+ this.name = name; // as a lookup "key"
+ this.hashcode = Arrays.hashCode(name);
+ return this;
+ }
+
+ 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) {
+ this.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) {
+ this.version = e.version;
+ this.name = e.name;
+ 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;
+ }
+
+ boolean isDir() {
+ return name != null &&
+ (name.length == 0 ||
+ name[name.length - 1] == '/');
+ }
+
+ 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); // 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
+ writeInt(os, mtime); // last modification time
+ 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<InputStream> streams;
+ ExChannelCloser(Path path,
+ SeekableByteChannel ch,
+ Set<InputStream> 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 HashMap<IndexNode, IndexNode> dirs;
+ private IndexNode root;
+ private IndexNode addToDir(IndexNode child) {
+ IndexNode cinode = dirs.get(child);
+ if (cinode != null)
+ return cinode;
+
+ byte[] cname = child.name;
+ byte[] pname = getParent(cname);
+ IndexNode pinode;
+
+ if (pname != null)
+ pinode = addToDir(IndexNode.keyOf(pname));
+ else
+ pinode = root;
+ cinode = inodes.get(child);
+ if (cname[cname.length -1] != '/') { // not a dir
+ cinode.sibling = pinode.child;
+ pinode.child = cinode;
+ return null;
+ }
+ //cinode = dirs.get(child);
+ if (cinode == null) // pseudo directry entry
+ cinode = new IndexNode(cname, -1);
+ cinode.sibling = pinode.child;
+ pinode.child = cinode;
+
+ dirs.put(child, cinode);
+ return cinode;
+ }
+
+ private HashMap<IndexNode, IndexNode> getDirs()
+ throws IOException
+ {
+ beginWrite();
+ try {
+ if (dirs != null)
+ return dirs;
+ dirs = new HashMap<>();
+ root = new IndexNode(new byte[0], -1);
+ dirs.put(root, root);
+ for (IndexNode node : inodes.keySet())
+ addToDir(node);
+ return dirs;
+ } finally {
+ endWrite();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipFileSystemProvider.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.file.FileRef;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.FileSystemAlreadyExistsException;
+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.FileAttribute;
+import java.nio.file.spi.FileSystemProvider;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/*
+ *
+ * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
+ */
+
+public class ZipFileSystemProvider extends FileSystemProvider {
+
+
+ private final Map<Path, ZipFileSystem> filesystems = new HashMap<>();
+
+ public ZipFileSystemProvider() {}
+
+ @Override
+ public String getScheme() {
+ return "jar";
+ }
+
+ 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.getSchemeSpecificPart();
+ 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);
+ }
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env)
+ throws IOException
+ {
+ return newFileSystem(uriToPath(uri), env);
+ }
+
+ @Override
+ public FileSystem newFileSystem(FileRef file, Map<String, ?> env)
+ throws IOException
+ {
+ if (!(file instanceof Path))
+ throw new UnsupportedOperationException();
+ Path path = (Path)file;
+ if (!path.toUri().getScheme().equalsIgnoreCase("file")) {
+ throw new UnsupportedOperationException();
+ }
+ return newFileSystem(path, env);
+ }
+
+ private FileSystem newFileSystem(Path path, Map<String, ?> env)
+ throws IOException
+ {
+ synchronized(filesystems) {
+ Path realPath = null;
+ if (path.exists()) {
+ realPath = path.toRealPath(true);
+ if (filesystems.containsKey(realPath))
+ throw new FileSystemAlreadyExistsException();
+ }
+ ZipFileSystem zipfs = new ZipFileSystem(this, path, env);
+ if (realPath == null)
+ realPath = path.toRealPath(true);
+ filesystems.put(realPath, zipfs);
+ return zipfs;
+ }
+ }
+
+ @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 FileChannel newFileChannel(Path path,
+ Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ if (path == null)
+ throw new NullPointerException("path is null");
+ if (path instanceof ZipPath)
+ return ((ZipPath)path).newFileChannel(options, attrs);
+ throw new ProviderMismatchException();
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ synchronized (filesystems) {
+ ZipFileSystem zipfs = null;
+ try {
+ zipfs = filesystems.get(uriToPath(uri).toRealPath(true));
+ } catch (IOException x) {
+ // ignore the ioe from toRealPath(), return FSNFE
+ }
+ if (zipfs == null)
+ throw new FileSystemNotFoundException();
+ return zipfs;
+ }
+ }
+
+ void removeFileSystem(Path zfpath) throws IOException {
+ synchronized (filesystems) {
+ filesystems.remove(zfpath.toRealPath(true));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipInfo.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Map;
+import static com.sun.nio.zipfs.ZipConstants.*;
+import static com.sun.nio.zipfs.ZipUtils.*;
+
+/**
+ * Print all loc and cen headers of the ZIP file
+ *
+ * @author Xueming Shen
+ */
+
+public class ZipInfo {
+
+ public static void main(String[] args) throws Throwable {
+ if (args.length < 1) {
+ print("Usage: java ZipInfo zfname");
+ } else {
+ Map<String, ?> env = Collections.emptyMap();
+ ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider()
+ .newFileSystem(Paths.get(args[0]), env));
+ byte[] cen = zfs.cen;
+ if (cen == null) {
+ print("zip file is empty%n");
+ return;
+ }
+ int pos = 0;
+ byte[] buf = new byte[1024];
+ int no = 1;
+ while (pos + CENHDR < cen.length) {
+ print("----------------#%d--------------------%n", no++);
+ printCEN(cen, pos);
+
+ // use size CENHDR as the extra bytes to read, just in case the
+ // loc.extra is bigger than the cen.extra, try to avoid to read
+ // twice
+ long len = LOCHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENHDR;
+ if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
+ zfs.zerror("read loc header failed");
+ if (LOCEXT(buf) > CENEXT(cen, pos) + CENHDR) {
+ // have to read the second time;
+ len = LOCHDR + LOCNAM(buf) + LOCEXT(buf);
+ if (zfs.readFullyAt(buf, 0, len, locoff(cen, pos)) != len)
+ zfs.zerror("read loc header failed");
+ }
+ printLOC(buf);
+ pos += CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos);
+ }
+ zfs.close();
+ }
+ }
+
+ static void print(String fmt, Object... objs) {
+ System.out.printf(fmt, objs);
+ }
+
+ static void printLOC(byte[] loc) {
+ print("%n");
+ print("[Local File Header]%n");
+ print(" Signature : %#010x%n", LOCSIG(loc));
+ if (LOCSIG(loc) != LOCSIG) {
+ print(" Wrong signature!");
+ return;
+ }
+ print(" Version : %#6x [%d.%d]%n",
+ LOCVER(loc), LOCVER(loc) / 10, LOCVER(loc) % 10);
+ print(" Flag : %#6x%n", LOCFLG(loc));
+ print(" Method : %#6x%n", LOCHOW(loc));
+ print(" LastMTime : %#10x [%tc]%n",
+ LOCTIM(loc), dosToJavaTime(LOCTIM(loc)));
+ print(" CRC : %#10x%n", LOCCRC(loc));
+ print(" CSize : %#10x%n", LOCSIZ(loc));
+ print(" Size : %#10x%n", LOCLEN(loc));
+ print(" NameLength : %#6x [%s]%n",
+ LOCNAM(loc), new String(loc, LOCHDR, LOCNAM(loc)));
+ print(" ExtraLength : %#6x%n", LOCEXT(loc));
+ if (LOCEXT(loc) != 0)
+ printExtra(loc, LOCHDR + LOCNAM(loc), LOCEXT(loc));
+ }
+
+ static void printCEN(byte[] cen, int off) {
+ print("[Central Directory Header]%n");
+ print(" Signature : %#010x%n", CENSIG(cen, off));
+ if (CENSIG(cen, off) != CENSIG) {
+ print(" Wrong signature!");
+ return;
+ }
+ print(" VerMadeby : %#6x [%d, %d.%d]%n",
+ CENVEM(cen, off), (CENVEM(cen, off) >> 8),
+ (CENVEM(cen, off) & 0xff) / 10,
+ (CENVEM(cen, off) & 0xff) % 10);
+ print(" VerExtract : %#6x [%d.%d]%n",
+ CENVER(cen, off), CENVER(cen, off) / 10, CENVER(cen, off) % 10);
+ print(" Flag : %#6x%n", CENFLG(cen, off));
+ print(" Method : %#6x%n", CENHOW(cen, off));
+ print(" LastMTime : %#10x [%tc]%n",
+ CENTIM(cen, off), dosToJavaTime(CENTIM(cen, off)));
+ print(" CRC : %#10x%n", CENCRC(cen, off));
+ print(" CSize : %#10x%n", CENSIZ(cen, off));
+ print(" Size : %#10x%n", CENLEN(cen, off));
+ print(" NameLen : %#6x [%s]%n",
+ CENNAM(cen, off), new String(cen, off + CENHDR, CENNAM(cen, off)));
+ print(" ExtraLen : %#6x%n", CENEXT(cen, off));
+ if (CENEXT(cen, off) != 0)
+ printExtra(cen, off + CENHDR + CENNAM(cen, off), CENEXT(cen, off));
+ print(" CommentLen : %#6x%n", CENCOM(cen, off));
+ print(" DiskStart : %#6x%n", CENDSK(cen, off));
+ print(" Attrs : %#6x%n", CENATT(cen, off));
+ print(" AttrsEx : %#10x%n", CENATX(cen, off));
+ print(" LocOff : %#10x%n", CENOFF(cen, off));
+
+ }
+
+ static long locoff(byte[] cen, int pos) {
+ long locoff = CENOFF(cen, pos);
+ if (locoff == ZIP64_MINVAL) { //ZIP64
+ int off = pos + CENHDR + CENNAM(cen, pos);
+ int end = off + CENEXT(cen, pos);
+ while (off + 4 < end) {
+ int tag = SH(cen, off);
+ int sz = SH(cen, off + 2);
+ if (tag != EXTID_ZIP64) {
+ off += 4 + sz;
+ continue;
+ }
+ off += 4;
+ if (CENLEN(cen, pos) == ZIP64_MINVAL)
+ off += 8;
+ if (CENSIZ(cen, pos) == ZIP64_MINVAL)
+ off += 8;
+ return LL(cen, off);
+ }
+ // should never be here
+ }
+ return locoff;
+ }
+
+ static void printExtra(byte[] extra, int off, int len) {
+ int end = off + len;
+ while (off + 4 <= end) {
+ int tag = SH(extra, off);
+ int sz = SH(extra, off + 2);
+ print(" [tag=0x%04x, sz=%d, data= ", tag, sz);
+ if (off + sz > end) {
+ print(" Error: Invalid extra data, beyond extra length");
+ break;
+ }
+ off += 4;
+ for (int i = 0; i < sz; i++)
+ print("%02x ", extra[off + i]);
+ print("]%n");
+ switch (tag) {
+ case EXTID_ZIP64 :
+ print(" ->ZIP64: ");
+ int pos = off;
+ while (pos + 8 <= off + sz) {
+ print(" *0x%x ", LL(extra, pos));
+ pos += 8;
+ }
+ print("%n");
+ break;
+ case EXTID_NTFS:
+ print(" ->PKWare NTFS%n");
+ // 4 bytes reserved
+ if (SH(extra, off + 4) != 0x0001 || SH(extra, off + 6) != 24)
+ print(" Error: Invalid NTFS sub-tag or subsz");
+ print(" mtime:%tc%n",
+ winToJavaTime(LL(extra, off + 8)));
+ print(" atime:%tc%n",
+ winToJavaTime(LL(extra, off + 16)));
+ print(" ctime:%tc%n",
+ winToJavaTime(LL(extra, off + 24)));
+ break;
+ case EXTID_EXTT:
+ print(" ->Inof-ZIP Extended Timestamp: flag=%x%n",extra[off]);
+ pos = off + 1 ;
+ while (pos + 4 <= off + sz) {
+ print(" *%tc%n",
+ unixToJavaTime(LG(extra, pos)));
+ pos += 4;
+ }
+ break;
+ default:
+ }
+ off += sz;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipPath.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,960 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+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.*;
+import java.nio.file.DirectoryStream.Filter;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.util.*;
+import static java.nio.file.StandardOpenOption.*;
+import static java.nio.file.StandardCopyOption.*;
+
+/**
+ *
+ * @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
+ */
+
+public class ZipPath extends 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 getName() {
+ 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(boolean resolveLinks) throws IOException {
+ ZipPath realPath = new ZipPath(zfs, getResolvedPath());
+ realPath.checkAccess();
+ return realPath;
+ }
+
+ @Override
+ public 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("jar",
+ 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 null;
+ 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[0] == '/');
+ }
+
+ @Override
+ public ZipPath resolve(Path other) {
+ if (other == null)
+ return this;
+ 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 ZipPath resolve(String other) {
+ return resolve(getFileSystem().getPath(other));
+ }
+
+ @Override
+ public boolean startsWith(Path other) {
+ final ZipPath o = checkPath(other);
+ if (o.isAbsolute() != this.isAbsolute())
+ return false;
+ final int oCount = o.getNameCount();
+ if (getNameCount() < oCount)
+ return false;
+ for (int i = 0; i < oCount; i++) {
+ if (!o.getName(i).equals(getName(i)))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean endsWith(Path other) {
+ final ZipPath o = checkPath(other);
+ if (o.isAbsolute())
+ return this.isAbsolute() ? this.equals(o) : false;
+ int i = o.getNameCount();
+ int j = this.getNameCount();
+ if (j < i)
+ return false;
+ for (--i, --j; i >= 0; i--, j--) {
+ if (!o.getName(i).equals(this.getName(j)))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public Path normalize() {
+ byte[] resolved = getResolved();
+ if (resolved == path) // no change
+ return this;
+ if (resolved.length == 0)
+ return null;
+ 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)'.')
+ 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;
+ }
+
+ @Override
+ public Path createSymbolicLink(
+ Path target, FileAttribute<?>... attrs) throws IOException {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+
+ @Override
+ public Path createLink(
+ Path existing) throws IOException {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+
+ @Override
+ public Path readSymbolicLink() throws IOException {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+
+ @Override
+ public Path createDirectory(FileAttribute<?>... attrs)
+ throws IOException
+ {
+ zfs.createDirectory(getResolvedPath(), attrs);
+ return this;
+ }
+
+ public final Path createFile(FileAttribute<?>... attrs)
+ throws IOException
+ {
+ OutputStream os = newOutputStream(CREATE_NEW, WRITE);
+ try {
+ os.close();
+ } catch (IOException x) {}
+ return this;
+ }
+
+ @Override
+ public 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());
+ }
+
+ private static final DirectoryStream.Filter<Path> acceptAllFilter =
+ new DirectoryStream.Filter<>() {
+ @Override public boolean accept(Path entry) { return true; }
+ };
+
+ @Override
+ public final DirectoryStream<Path> newDirectoryStream() throws IOException {
+ return newDirectoryStream(acceptAllFilter);
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
+ throws IOException
+ {
+ return new ZipDirectoryStream(this, filter);
+ }
+
+ @Override
+ public final DirectoryStream<Path> newDirectoryStream(String glob)
+ throws IOException
+ {
+ // avoid creating a matcher if all entries are required.
+ if (glob.equals("*"))
+ return newDirectoryStream();
+
+ // create a matcher and return a filter that uses it.
+ final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob);
+ DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<>() {
+ @Override
+ public boolean accept(Path entry) {
+ return matcher.matches(entry.getName());
+ }
+ };
+ return newDirectoryStream(filter);
+ }
+
+ @Override
+ public final void delete() throws IOException {
+ zfs.deleteFile(getResolvedPath(), true);
+ }
+
+ @Override
+ public final 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;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V extends FileAttributeView> V getFileAttributeView(Class<V> type,
+ LinkOption... options)
+ {
+ return (V)ZipFileAttributeView.get(this, type);
+ }
+
+ @Override
+ public 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);
+ }
+
+ private Object getAttributesImpl(String attribute, boolean domap)
+ throws IOException
+ {
+ String view = null;
+ String attr = null;
+ int colonPos = attribute.indexOf(':');
+ if (colonPos == -1) {
+ view = "basic";
+ attr = attribute;
+ } else {
+ view = attribute.substring(0, colonPos++);
+ attr = attribute.substring(colonPos);
+ }
+ ZipFileAttributeView zfv = ZipFileAttributeView.get(this, view);
+ if (zfv == null) {
+ throw new UnsupportedOperationException("view not supported");
+ }
+ return zfv.getAttribute(attr, domap);
+ }
+
+ @Override
+ public Object getAttribute(String attribute, LinkOption... options)
+ throws IOException
+ {
+ return getAttributesImpl(attribute, false);
+ }
+
+ @Override
+ public Map<String,?> readAttributes(String attribute, LinkOption... options)
+ throws IOException
+ {
+ return (Map<String, ?>)getAttributesImpl(attribute, true);
+ }
+
+ @Override
+ public 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));
+ }
+
+ @Override
+ public boolean isSameFile(Path other) throws IOException {
+ if (other == null ||
+ this.getFileSystem() != other.getFileSystem())
+ return false;
+ this.checkAccess();
+ other.checkAccess();
+ return Arrays.equals(this.getResolvedPath(),
+ ((ZipPath)other).getResolvedPath());
+ }
+
+ 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 Iterator<Path> 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();
+ }
+ };
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ return zfs.newByteChannel(getResolvedPath(), options, attrs);
+ }
+
+
+ FileChannel newFileChannel(Set<? extends OpenOption> options,
+ FileAttribute<?>... attrs)
+ throws IOException
+ {
+ return zfs.newFileChannel(getResolvedPath(), options, attrs);
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(OpenOption... options)
+ throws IOException {
+ Set<OpenOption> set = new HashSet<>(options.length);
+ Collections.addAll(set, options);
+ return newByteChannel(set);
+ }
+
+ @Override
+ public 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());
+
+ }
+
+ @Override
+ public boolean exists() {
+ if (path.length == 1 && path[0] == '/')
+ return true;
+ try {
+ return zfs.exists(getResolvedPath());
+ } catch (IOException x) {}
+ return false;
+ }
+
+ @Override
+ public boolean notExists() {
+ return !exists();
+ }
+
+
+ @Override
+ public OutputStream newOutputStream(OpenOption... options)
+ throws IOException
+ {
+ if (options.length == 0)
+ return zfs.newOutputStream(getResolvedPath(),
+ CREATE_NEW, WRITE);
+ return zfs.newOutputStream(getResolvedPath(), options);
+ }
+
+ @Override
+ public Path moveTo(Path target, CopyOption... options)
+ throws IOException
+ {
+ if (this.zfs.provider() == target.getFileSystem().provider() &&
+ this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile()))
+ {
+ zfs.copyFile(true,
+ getResolvedPath(),
+ ((ZipPath)target).getResolvedPath(),
+ options);
+ } else {
+ copyToTarget(target, options);
+ delete();
+ }
+ return target;
+ }
+
+ @Override
+ public Path copyTo(Path target, CopyOption... options)
+ throws IOException
+ {
+ if (this.zfs.provider() == target.getFileSystem().provider() &&
+ this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile()))
+ {
+ zfs.copyFile(false,
+ getResolvedPath(),
+ ((ZipPath)target).getResolvedPath(),
+ options);
+ } else {
+ copyToTarget(target, options);
+ }
+ return target;
+ }
+
+ private void copyToTarget(Path 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 =
+ target.getFileAttributeView(BasicFileAttributeView.class);
+ try {
+ view.setTimes(zfas.lastModifiedTime(),
+ zfas.lastAccessTime(),
+ zfas.creationTime());
+ } catch (IOException x) {
+ // rollback?
+ try {
+ target.delete();
+ } catch (IOException ignore) { }
+ throw x;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipUtils.java Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.sun.nio.zipfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.regex.PatternSyntaxException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ * @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();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/demo/zipfs Mon Dec 06 13:18:16 2010 -0800
@@ -0,0 +1,703 @@
+/*
+ * Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.net.*;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static java.nio.file.StandardOpenOption.*;
+import static java.nio.file.StandardCopyOption.*;
+
+/*
+ * ZipFileSystem usage demo
+ *
+ * java Demo action ZipfileName [...]
+ *
+ * @author Xueming Shen
+ */
+
+public class Demo {
+
+ static enum Action {
+ rename, // <java Demo rename zipfile src dst>
+ // rename entry src to dst inside zipfile
+
+ movein, // <java Demo movein zipfile src dst>
+ // move an external src file into zipfile
+ // as entry dst
+
+ moveout, // <java Demo moveout zipfile src dst>
+ // move a zipfile entry src out to dst
+
+ copy, // <java Demo copy zipfile src dst>
+ // copy entry src to dst inside zipfile
+
+ copyin, // <java Demo copyin zipfile src dst>
+ // copy an external src file into zipfile
+ // as entry dst
+
+ copyin_attrs, // <java Demo copyin_attrs zipfile src dst>
+ // copy an external src file into zipfile
+ // as entry dst, with attributes (timestamp)
+
+ copyout, // <java Demo copyout zipfile src dst>
+ // copy zipfile entry src" out to file dst
+
+ copyout_attrs, // <java Demo copyout_attrs zipfile src dst>
+
+ zzmove, // <java Demo zzmove zfsrc zfdst path>
+ // move entry path/dir from zfsrc to zfdst
+
+ zzcopy, // <java Demo zzcopy zfsrc zfdst path>
+ // copy path from zipfile zfsrc to zipfile
+ // zfdst
+
+ attrs, // <java Demo attrs zipfile path>
+ // printout the attributes of entry path
+
+ attrsspace, // <java Demo attrsspace zipfile path>
+ // printout the storespace attrs of entry path
+
+ setmtime, // <java Demo setmtime zipfile "MM/dd/yy-HH:mm:ss" path...>
+ // set the lastModifiedTime of entry path
+
+ setatime, // <java Demo setatime zipfile "MM/dd/yy-HH:mm:ss" path...>
+ setctime, // <java Demo setctime zipfile "MM/dd/yy-HH:mm:ss" path...>
+
+ lsdir, // <java Demo lsdir zipfile dir>
+ // list dir's direct child files/dirs
+
+ mkdir, // <java Demo mkdir zipfile dir>
+
+ mkdirs, // <java Demo mkdirs zipfile dir>
+
+ rmdirs, // <java Demo rmdirs zipfile dir>
+
+ list, // <java Demo list zipfile [dir]>
+ // recursively list all entries of dir
+ // via DirectoryStream
+
+ tlist, // <java Demo tlist zipfile [dir]>
+ // list with buildDirTree=true
+
+ vlist, // <java Demo vlist zipfile [dir]>
+ // recursively verbose list all entries of
+ // dir via DirectoryStream
+
+ walk, // <java Demo walk zipfile [dir]>
+ // recursively walk all entries of dir
+ // via Files.walkFileTree
+
+ twalk, // <java Demo twalk zipfile [dir]>
+ // walk with buildDirTree=true
+
+ extract, // <java Demo extract zipfile file [...]>
+
+ update, // <java Demo extract zipfile file [...]>
+
+ delete, // <java Demo delete zipfile file [...]>
+
+ add, // <java Demo add zipfile file [...]>
+
+ create, // <java Demo create zipfile file [...]>
+ // create a new zipfile if it doesn't exit
+ // and then add the file(s) into it.
+
+ attrs2, // <java Demo attrs2 zipfile file [...]>
+ // test different ways to print attrs
+
+ prof,
+ }
+
+ public static void main(String[] args) throws Throwable {
+
+ Action action = Action.valueOf(args[0]);
+ Map<String, Object> env = env = new HashMap<>();
+ if (action == Action.create)
+ env.put("create", "true");
+ if (action == Action.tlist || action == Action.twalk)
+ env.put("buildDirTree", true);
+ FileSystem fs = FileSystems.newFileSystem(Paths.get(args[1]), env, null);
+
+ try {
+ FileSystem fs2;
+ Path path, src, dst;
+ boolean isRename = false;
+ switch (action) {
+ case rename:
+ src = fs.getPath(args[2]);
+ dst = fs.getPath(args[3]);
+ src.moveTo(dst);
+ break;
+ case moveout:
+ src = fs.getPath(args[2]);
+ dst = Paths.get(args[3]);
+ src.moveTo(dst);
+ break;
+ case movein:
+ src = Paths.get(args[2]);
+ dst = fs.getPath(args[3]);
+ src.moveTo(dst);
+ break;
+ case copy:
+ src = fs.getPath(args[2]);
+ dst = fs.getPath(args[3]);
+ src.copyTo(dst);
+ break;
+ case copyout:
+ src = fs.getPath(args[2]);
+ dst = Paths.get(args[3]);
+ src.copyTo(dst);
+ break;
+ case copyin:
+ src = Paths.get(args[2]);
+ dst = fs.getPath(args[3]);
+ src.copyTo(dst);
+ break;
+ case copyin_attrs:
+ src = Paths.get(args[2]);
+ dst = fs.getPath(args[3]);
+ src.copyTo(dst, COPY_ATTRIBUTES);
+ break;
+ case copyout_attrs:
+ src = fs.getPath(args[2]);
+ dst = Paths.get(args[3]);
+ src.copyTo(dst, COPY_ATTRIBUTES);
+ break;
+ case zzmove:
+ fs2 = FileSystems.newFileSystem(Paths.get(args[2]), env, null);
+ //sf1.getPath(args[3]).moveTo(fs2.getPath(args[3]));
+ z2zmove(fs, fs2, args[3]);
+ fs2.close();
+ break;
+ case zzcopy:
+ fs2 = FileSystems.newFileSystem(Paths.get(args[2]), env, null);
+ //sf1.getPath(args[3]).copyTo(fs2.getPath(args[3]));
+ z2zcopy(fs, fs2, args[3]);
+ fs2.close();
+ break;
+ case attrs:
+ for (int i = 2; i < args.length; i++) {
+ path = fs.getPath(args[i]);
+ System.out.println(path);
+ System.out.println(
+ Attributes.readBasicFileAttributes(path).toString());
+ }
+ break;
+ case setmtime:
+ DateFormat df = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
+ Date newDatetime = df.parse(args[2]);
+ for (int i = 3; i < args.length; i++) {
+ path = fs.getPath(args[i]);
+ path.setAttribute("lastModifiedTime",
+ FileTime.fromMillis(newDatetime.getTime()));
+ System.out.println(
+ Attributes.readBasicFileAttributes(path).toString());
+ }
+ break;
+ case setctime:
+ df = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
+ newDatetime = df.parse(args[2]);
+ for (int i = 3; i < args.length; i++) {
+ path = fs.getPath(args[i]);
+ path.setAttribute("creationTime",
+ FileTime.fromMillis(newDatetime.getTime()));
+ System.out.println(
+ Attributes.readBasicFileAttributes(path).toString());
+ }
+ break;
+ case setatime:
+ df = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
+ newDatetime = df.parse(args[2]);
+ for (int i = 3; i < args.length; i++) {
+ path = fs.getPath(args[i]);
+ path.setAttribute("lastAccessTime",
+ FileTime.fromMillis(newDatetime.getTime()));
+ System.out.println(
+ Attributes.readBasicFileAttributes(path).toString());
+ }
+ break;
+ case attrsspace:
+ path = fs.getPath("/");
+ FileStore fstore = path.getFileStore();
+ //System.out.println(fstore.getFileStoreAttributeView(FileStoreSpaceAttributeView.class)
+ // .readAttributes());
+ // or
+ System.out.printf("filestore[%s]%n", fstore.name());
+ System.out.printf(" totalSpace: %d%n",
+ (Long)fstore.getAttribute("space:totalSpace"));
+ System.out.printf(" usableSpace: %d%n",
+ (Long)fstore.getAttribute("space:usableSpace"));
+ System.out.printf(" unallocSpace: %d%n",
+ (Long)fstore.getAttribute("space:unallocatedSpace"));
+ break;
+ case list:
+ case tlist:
+ if (args.length < 3)
+ list(fs.getPath("/"), false);
+ else
+ list(fs.getPath(args[2]), false);
+ break;
+ case vlist:
+ if (args.length < 3)
+ list(fs.getPath("/"), true);
+ else
+ list(fs.getPath(args[2]), true);
+ break;
+ case twalk:
+ case walk:
+ walk(fs.getPath((args.length > 2)? args[2] : "/"));
+ break;
+ case extract:
+ if (args.length == 2) {
+ extract(fs, "/");
+ } else {
+ for (int i = 2; i < args.length; i++) {
+ extract(fs, args[i]);
+ }
+ }
+ break;
+ case delete:
+ for (int i = 2; i < args.length; i++)
+ fs.getPath(args[i]).delete();
+ break;
+ case create:
+ case add:
+ case update:
+ for (int i = 2; i < args.length; i++) {
+ update(fs, args[i]);
+ }
+ break;
+ case lsdir:
+ path = fs.getPath(args[2]);
+ final String fStr = (args.length > 3)?args[3]:"";
+ DirectoryStream<Path> ds = path.newDirectoryStream(
+ new DirectoryStream.Filter<Path>() {
+ public boolean accept(Path path) {
+ return path.toString().contains(fStr);
+ }
+ });
+ for (Path p : ds)
+ System.out.println(p);
+ break;
+ case mkdir:
+ fs.getPath(args[2]).createDirectory();
+ break;
+ case mkdirs:
+ mkdirs(fs.getPath(args[2]));
+ break;
+ case attrs2:
+ for (int i = 2; i < args.length; i++) {
+ path = fs.getPath(args[i]);
+ System.out.printf("%n%s%n", path);
+ System.out.println("-------(1)---------");
+ System.out.println(
+ Attributes.readBasicFileAttributes(path).toString());
+ System.out.println("-------(2)---------");
+ Map<String, ?> map = path.readAttributes("zip:*");
+ for (Map.Entry<String, ?> e : map.entrySet()) {
+ System.out.printf(" %s : %s%n", e.getKey(), e.getValue());
+ }
+ System.out.println("-------(3)---------");
+ map = path.readAttributes("size,lastModifiedTime,isDirectory");
+ for (Map.Entry<String, ?> e : map.entrySet()) {
+ System.out.printf(" %s : %s%n", e.getKey(), e.getValue());
+ }
+ }
+ break;
+ case prof:
+ list(fs.getPath("/"), false);
+ while (true) {
+ Thread.sleep(10000);
+ //list(fs.getPath("/"), true);
+ System.out.println("sleeping...");
+ }
+ }
+ } catch (Exception x) {
+ x.printStackTrace();
+ } finally {
+ if (fs != null)
+ fs.close();
+ }
+ }
+
+ private static byte[] getBytes(String name) {
+ return name.getBytes();
+ }
+
+ private static String getString(byte[] name) {
+ return new String(name);
+ }
+
+ private static void walk(Path path) throws IOException
+ {
+ Files.walkFileTree(
+ path,
+ new SimpleFileVisitor<Path>() {
+ private int indent = 0;
+ private void indent() {
+ int n = 0;
+ while (n++ < indent)
+ System.out.printf(" ");
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file,
+ BasicFileAttributes attrs)
+ {
+ indent();
+ System.out.printf("%s%n", file.getName().toString());
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir,
+ BasicFileAttributes attrs)
+ {
+ indent();
+ System.out.printf("[%s]%n", dir.toString());
+ indent += 2;
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir,
+ IOException ioe)
+ {
+ indent -= 2;
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ private static void update(FileSystem fs, String path) throws Throwable{
+ Path src = FileSystems.getDefault().getPath(path);
+ if (Boolean.TRUE.equals(src.getAttribute("isDirectory"))) {
+ DirectoryStream<Path> ds = src.newDirectoryStream();
+ for (Path child : ds)
+ update(fs, child.toString());
+ ds.close();
+ } else {
+ Path dst = fs.getPath(path);
+ Path parent = dst.getParent();
+ if (parent != null && parent.notExists())
+ mkdirs(parent);
+ src.copyTo(dst, REPLACE_EXISTING);
+ }
+ }
+
+ private static void extract(FileSystem fs, String path) throws Throwable{
+ Path src = fs.getPath(path);
+ if (Boolean.TRUE.equals(src.getAttribute("isDirectory"))) {
+ DirectoryStream<Path> ds = src.newDirectoryStream();
+ for (Path child : ds)
+ extract(fs, child.toString());
+ ds.close();
+ } else {
+ if (path.startsWith("/"))
+ path = path.substring(1);
+ Path dst = FileSystems.getDefault().getPath(path);
+ Path parent = dst.getParent();
+ if (parent.notExists())
+ mkdirs(parent);
+ src.copyTo(dst, REPLACE_EXISTING);
+ }
+ }
+
+ // use DirectoryStream
+ private static void z2zcopy(FileSystem src, FileSystem dst, String path)
+ throws IOException
+ {
+ Path srcPath = src.getPath(path);
+ Path dstPath = dst.getPath(path);
+
+ if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) {
+ if (!dstPath.exists()) {
+ try {
+ mkdirs(dstPath);
+ } catch (FileAlreadyExistsException x) {}
+ }
+ DirectoryStream<Path> ds = srcPath.newDirectoryStream();
+ for (Path child : ds) {
+ z2zcopy(src, dst,
+ path + (path.endsWith("/")?"":"/") + child.getName());
+ }
+ ds.close();
+ } else {
+ //System.out.println("copying..." + path);
+ srcPath.copyTo(dstPath);
+ }
+ }
+
+ // use TreeWalk to move
+ private static void z2zmove(FileSystem src, FileSystem dst, String path)
+ throws IOException
+ {
+ final Path srcPath = src.getPath(path).toAbsolutePath();
+ final Path dstPath = dst.getPath(path).toAbsolutePath();
+
+ Files.walkFileTree(srcPath, new SimpleFileVisitor<Path>() {
+
+ @Override
+ public FileVisitResult visitFile(Path file,
+ BasicFileAttributes attrs)
+ {
+ Path dst = srcPath.relativize(file);
+ dst = dstPath.resolve(dst);
+ try {
+ Path parent = dstPath.getParent();
+ if (parent != null && parent.notExists())
+ mkdirs(parent);
+ file.moveTo(dst);
+ } catch (IOException x) {
+ x.printStackTrace();
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir,
+ BasicFileAttributes attrs)
+ {
+ Path dst = srcPath.relativize(dir);
+ dst = dstPath.resolve(dst);
+ try {
+
+ if (dst.notExists())
+ mkdirs(dst);
+ } catch (IOException x) {
+ x.printStackTrace();
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path dir,
+ IOException ioe)
+ throws IOException
+ {
+ try {
+ dir.delete();
+ } catch (IOException x) {
+ //x.printStackTrace();
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+
+ }
+
+ private static void mkdirs(Path path) throws IOException {
+ path = path.toAbsolutePath();
+ Path parent = path.getParent();
+ if (parent != null) {
+ if (parent.notExists())
+ mkdirs(parent);
+ }
+ path.createDirectory();
+ }
+
+ private static void rmdirs(Path path) throws IOException {
+ while (path != null && path.getNameCount() != 0) {
+ path.delete();
+ path = path.getParent();
+ }
+ }
+
+ private static void list(Path path, boolean verbose ) throws IOException {
+ if (!"/".equals(path.toString())) {
+ System.out.printf(" %s%n", path.toString());
+ if (verbose)
+ System.out.println(Attributes.readBasicFileAttributes(path).toString());
+ }
+ if (path.notExists())
+ return;
+ if (Attributes.readBasicFileAttributes(path).isDirectory()) {
+ DirectoryStream<Path> ds = path.newDirectoryStream();
+ for (Path child : ds)
+ list(child, verbose);
+ ds.close();
+ }
+ }
+
+ // check the content of two paths are equal
+ private static void checkEqual(Path src, Path dst) throws IOException
+ {
+ //System.out.printf("checking <%s> vs <%s>...%n",
+ // src.toString(), dst.toString());
+
+ //streams
+ InputStream isSrc = src.newInputStream();
+ InputStream isDst = dst.newInputStream();
+ byte[] bufSrc = new byte[8192];
+ byte[] bufDst = new byte[8192];
+
+ try {
+ int nSrc = 0;
+ while ((nSrc = isSrc.read(bufSrc)) != -1) {
+ int nDst = 0;
+ while (nDst < nSrc) {
+ int n = isDst.read(bufDst, nDst, nSrc - nDst);
+ if (n == -1) {
+ System.out.printf("checking <%s> vs <%s>...%n",
+ src.toString(), dst.toString());
+ throw new RuntimeException("CHECK FAILED!");
+ }
+ nDst += n;
+ }
+ while (--nSrc >= 0) {
+ if (bufSrc[nSrc] != bufDst[nSrc]) {
+ System.out.printf("checking <%s> vs <%s>...%n",
+ src.toString(), dst.toString());
+ throw new RuntimeException("CHECK FAILED!");
+ }
+ nSrc--;
+ }
+ }
+ } finally {
+ isSrc.close();
+ isDst.close();
+ }
+
+ // channels
+ SeekableByteChannel chSrc = src.newByteChannel();
+ SeekableByteChannel chDst = dst.newByteChannel();
+ if (chSrc.size() != chDst.size()) {
+ System.out.printf("src[%s].size=%d, dst[%s].size=%d%n",
+ chSrc.toString(), chSrc.size(),
+ chDst.toString(), chDst.size());
+ throw new RuntimeException("CHECK FAILED!");
+ }
+ ByteBuffer bbSrc = ByteBuffer.allocate(8192);
+ ByteBuffer bbDst = ByteBuffer.allocate(8192);
+
+ try {
+ int nSrc = 0;
+ while ((nSrc = chSrc.read(bbSrc)) != -1) {
+ int nDst = chDst.read(bbDst);
+ if (nSrc != nDst) {
+ System.out.printf("checking <%s> vs <%s>...%n",
+ src.toString(), dst.toString());
+ throw new RuntimeException("CHECK FAILED!");
+ }
+ while (--nSrc >= 0) {
+ if (bbSrc.get(nSrc) != bbDst.get(nSrc)) {
+ System.out.printf("checking <%s> vs <%s>...%n",
+ src.toString(), dst.toString());
+ throw new RuntimeException("CHECK FAILED!");
+ }
+ nSrc--;
+ }
+ bbSrc.flip();
+ bbDst.flip();
+ }
+ } catch (IOException x) {
+ x.printStackTrace();
+ } finally {
+ chSrc.close();
+ chDst.close();
+ }
+ }
+
+ private static void fchCopy(Path src, Path dst) throws IOException
+ {
+ Set<OpenOption> read = new HashSet<>();
+ read.add(READ);
+ Set<OpenOption> openwrite = new HashSet<>();
+ openwrite.add(CREATE_NEW);
+ openwrite.add(WRITE);
+
+ FileChannel srcFc = src.getFileSystem()
+ .provider()
+ .newFileChannel(src, read);
+ FileChannel dstFc = dst.getFileSystem()
+ .provider()
+ .newFileChannel(dst, openwrite);
+
+ try {
+ ByteBuffer bb = ByteBuffer.allocate(8192);
+ while (srcFc.read(bb) >= 0) {
+ bb.flip();
+ dstFc.write(bb);
+ bb.clear();
+ }
+ } finally {
+ srcFc.close();
+ dstFc.close();
+ }
+ }
+
+ private static void chCopy(Path src, Path dst) throws IOException
+ {
+ Set<OpenOption> read = new HashSet<>();
+ read.add(READ);
+ Set<OpenOption> openwrite = new HashSet<>();
+ openwrite.add(CREATE_NEW);
+ openwrite.add(WRITE);
+
+ SeekableByteChannel srcCh = src.newByteChannel(read);
+ SeekableByteChannel dstCh = dst.newByteChannel(openwrite);
+
+ try {
+ ByteBuffer bb = ByteBuffer.allocate(8192);
+ while (srcCh.read(bb) >= 0) {
+ bb.flip();
+ dstCh.write(bb);
+ bb.clear();
+ }
+ } finally {
+ srcCh.close();
+ dstCh.close();
+ }
+ }
+
+ private static void streamCopy(Path src, Path dst) throws IOException
+ {
+ InputStream isSrc = src.newInputStream();
+ OutputStream osDst = dst.newOutputStream();
+ byte[] buf = new byte[8192];
+ try {
+ int n = 0;
+ while ((n = isSrc.read(buf)) != -1) {
+ osDst.write(buf, 0, n);
+ }
+ } finally {
+ isSrc.close();
+ osDst.close();
+ }
+ }
+}
--- a/jdk/test/demo/zipfs/Basic.java Mon Dec 06 18:52:23 2010 +0000
+++ b/jdk/test/demo/zipfs/Basic.java Mon Dec 06 13:18:16 2010 -0800
@@ -40,24 +40,24 @@
boolean found = false;
for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
- if (provider.getScheme().equalsIgnoreCase("zip")) {
+ if (provider.getScheme().equalsIgnoreCase("jar")) {
found = true;
break;
}
}
if (!found)
- throw new RuntimeException("'zip' provider not installed");
+ throw new RuntimeException("'jar' provider not installed");
// Test: FileSystems#newFileSystem(FileRef)
Map<String,?> env = new HashMap<String,Object>();
FileSystems.newFileSystem(zipfile, env, null).close();
// Test: FileSystems#newFileSystem(URI)
- URI uri = URI.create("zip" + zipfile.toUri().toString().substring(4));
+ URI uri = new URI("jar", zipfile.toUri().toString(), null);
FileSystem fs = FileSystems.newFileSystem(uri, env, null);
// Test: exercise toUri method
- String expected = uri.toString() + "#/foo";
+ String expected = uri.toString() + "!/foo";
String actual = fs.getPath("/foo").toUri().toString();
if (!actual.equals(expected)) {
throw new RuntimeException("toUri returned '" + actual +
--- a/jdk/test/demo/zipfs/ZipFSTester.java Mon Dec 06 18:52:23 2010 +0000
+++ b/jdk/test/demo/zipfs/ZipFSTester.java Mon Dec 06 13:18:16 2010 -0800
@@ -58,7 +58,7 @@
// clone a fs and test on it
Path tmpfsPath = getTempPath();
Map<String, Object> env = new HashMap<String, Object>();
- env.put("createNew", true);
+ env.put("create", "true");
FileSystem fs0 = newZipFileSystem(tmpfsPath, env);
z2zcopy(fs, fs0, "/", 0);
fs0.close(); // sync to file
@@ -147,7 +147,7 @@
// create a new filesystem, copy everything from fs
Map<String, Object> env = new HashMap<String, Object>();
- env.put("createNew", true);
+ env.put("create", "true");
FileSystem fs0 = newZipFileSystem(fs1Path, env);
final FileSystem fs2 = newZipFileSystem(fs2Path, env);
@@ -282,11 +282,7 @@
private static FileSystem newZipFileSystem(Path path, Map<String, ?> env)
throws IOException
{
- return FileSystems.newFileSystem(
- URI.create("zip" +
- path.toUri().toString().substring(4)),
- env,
- null);
+ return FileSystems.newFileSystem(path, env, null);
}
private static Path getTempPath() throws IOException