diff --git a/make/mkdemo/nio/zipfs/Makefile b/make/mkdemo/nio/zipfs/Makefile index 16e71a9ba45d7372db23f76b8857a43d8812a9a4..a2405030dc1b8e66e390f5ff3bcb657b2111a78f 100644 --- a/make/mkdemo/nio/zipfs/Makefile +++ b/make/mkdemo/nio/zipfs/Makefile @@ -42,3 +42,10 @@ DEMO_DESTDIR = $(DEMODIR)/nio/$(DEMONAME) # include $(BUILDDIR)/common/Demo.gmk +#EXTJAR = $(EXTDIR)/$(DEMONAME).jar +# +#all : build $(EXTJAR) +# +#$(EXTJAR) : $(DEMO_JAR) +# $(prep-target) +# $(CP) $(DEMO_JAR) $(EXTJAR) diff --git a/src/share/classes/java/lang/Readable.java b/src/share/classes/java/lang/Readable.java index c97d792cca8c7b65376829bbb4221a60c5b58a97..51279ef27ec186dcdaabc5bfd0d6afb927dee94b 100644 --- a/src/share/classes/java/lang/Readable.java +++ b/src/share/classes/java/lang/Readable.java @@ -44,11 +44,11 @@ public interface Readable { * rewinding of the buffer is performed. * * @param cb the buffer to read characters into - * @return @return The number of char values added to the buffer, + * @return The number of {@code char} values added to the buffer, * or -1 if this source of characters is at its end * @throws IOException if an I/O error occurs * @throws NullPointerException if cb is null - * @throws ReadOnlyBufferException if cb is a read only buffer + * @throws java.nio.ReadOnlyBufferException if cb is a read only buffer */ public int read(java.nio.CharBuffer cb) throws IOException; diff --git a/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java b/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java index 2a07216f4ad0434dfebb8c3986e3b71a38405d0d..13901e842f72a910e1d4c42aab9770741e6b9e36 100644 --- a/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java +++ b/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java @@ -126,10 +126,8 @@ public class LinkedBlockingDeque */ Node next; - Node(E x, Node p, Node n) { + Node(E x) { item = x; - prev = p; - next = n; } } @@ -199,7 +197,7 @@ public class LinkedBlockingDeque for (E e : c) { if (e == null) throw new NullPointerException(); - if (!linkLast(e)) + if (!linkLast(new Node(e))) throw new IllegalStateException("Deque full"); } } finally { @@ -211,38 +209,38 @@ public class LinkedBlockingDeque // Basic linking and unlinking operations, called only while holding lock /** - * Links e as first element, or returns false if full. + * Links node as first element, or returns false if full. */ - private boolean linkFirst(E e) { + private boolean linkFirst(Node node) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) return false; Node f = first; - Node x = new Node(e, null, f); - first = x; + node.next = f; + first = node; if (last == null) - last = x; + last = node; else - f.prev = x; + f.prev = node; ++count; notEmpty.signal(); return true; } /** - * Links e as last element, or returns false if full. + * Links node as last element, or returns false if full. */ - private boolean linkLast(E e) { + private boolean linkLast(Node node) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) return false; Node l = last; - Node x = new Node(e, l, null); - last = x; + node.prev = l; + last = node; if (first == null) - first = x; + first = node; else - l.next = x; + l.next = node; ++count; notEmpty.signal(); return true; @@ -339,10 +337,11 @@ public class LinkedBlockingDeque */ public boolean offerFirst(E e) { if (e == null) throw new NullPointerException(); + Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { - return linkFirst(e); + return linkFirst(node); } finally { lock.unlock(); } @@ -353,10 +352,11 @@ public class LinkedBlockingDeque */ public boolean offerLast(E e) { if (e == null) throw new NullPointerException(); + Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { - return linkLast(e); + return linkLast(node); } finally { lock.unlock(); } @@ -368,10 +368,11 @@ public class LinkedBlockingDeque */ public void putFirst(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); + Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { - while (!linkFirst(e)) + while (!linkFirst(node)) notFull.await(); } finally { lock.unlock(); @@ -384,10 +385,11 @@ public class LinkedBlockingDeque */ public void putLast(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); + Node node = new Node(e); final ReentrantLock lock = this.lock; lock.lock(); try { - while (!linkLast(e)) + while (!linkLast(node)) notFull.await(); } finally { lock.unlock(); @@ -401,11 +403,12 @@ public class LinkedBlockingDeque public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); + Node node = new Node(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - while (!linkFirst(e)) { + while (!linkFirst(node)) { if (nanos <= 0) return false; nanos = notFull.awaitNanos(nanos); @@ -423,11 +426,12 @@ public class LinkedBlockingDeque public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); + Node node = new Node(e); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - while (!linkLast(e)) { + while (!linkLast(node)) { if (nanos <= 0) return false; nanos = notFull.awaitNanos(nanos); @@ -955,7 +959,20 @@ public class LinkedBlockingDeque final ReentrantLock lock = this.lock; lock.lock(); try { - return super.toString(); + Node p = first; + if (p == null) + return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + E e = p.item; + sb.append(e == this ? "(this Collection)" : e); + p = p.next; + if (p == null) + return sb.append(']').toString(); + sb.append(',').append(' '); + } } finally { lock.unlock(); } @@ -1053,6 +1070,26 @@ public class LinkedBlockingDeque } } + /** + * Returns the successor node of the given non-null, but + * possibly previously deleted, node. + */ + private Node succ(Node n) { + // Chains of deleted nodes ending in null or self-links + // are possible if multiple interior nodes are removed. + for (;;) { + Node s = nextNode(n); + if (s == null) + return null; + else if (s.item != null) + return s; + else if (s == n) + return firstNode(); + else + n = s; + } + } + /** * Advances next. */ @@ -1061,16 +1098,7 @@ public class LinkedBlockingDeque lock.lock(); try { // assert next != null; - Node s = nextNode(next); - if (s == next) { - next = firstNode(); - } else { - // Skip over removed nodes. - // May be necessary if multiple interior Nodes are removed. - while (s != null && s.item == null) - s = nextNode(s); - next = s; - } + next = succ(next); nextItem = (next == null) ? null : next.item; } finally { lock.unlock(); diff --git a/src/share/classes/java/util/jar/JarInputStream.java b/src/share/classes/java/util/jar/JarInputStream.java index a22c9372bc8f15537c3508d532b2d8b7ef7bc546..15818c932a6841653c722db34696959c43dd323f 100644 --- a/src/share/classes/java/util/jar/JarInputStream.java +++ b/src/share/classes/java/util/jar/JarInputStream.java @@ -28,6 +28,7 @@ package java.util.jar; import java.util.zip.*; import java.io.*; import sun.security.util.ManifestEntryVerifier; +import sun.misc.JarIndex; /** * The JarInputStream class is used to read the contents of @@ -47,7 +48,8 @@ class JarInputStream extends ZipInputStream { private JarEntry first; private JarVerifier jv; private ManifestEntryVerifier mev; - + private final boolean doVerify; + private boolean tryManifest; /** * Creates a new JarInputStream and reads the optional @@ -72,25 +74,33 @@ class JarInputStream extends ZipInputStream { */ public JarInputStream(InputStream in, boolean verify) throws IOException { super(in); - JarEntry e = (JarEntry)super.getNextEntry(); + this.doVerify = verify; + // This implementation assumes the META-INF/MANIFEST.MF entry + // should be either the first or the second entry (when preceded + // by the dir META-INF/). It skips the META-INF/ and then + // "consumes" the MANIFEST.MF to initialize the Manifest object. + JarEntry e = (JarEntry)super.getNextEntry(); if (e != null && e.getName().equalsIgnoreCase("META-INF/")) e = (JarEntry)super.getNextEntry(); + first = checkManifest(e); + } + private JarEntry checkManifest(JarEntry e) + throws IOException + { if (e != null && JarFile.MANIFEST_NAME.equalsIgnoreCase(e.getName())) { man = new Manifest(); byte bytes[] = getBytes(new BufferedInputStream(this)); man.read(new ByteArrayInputStream(bytes)); - //man.read(new BufferedInputStream(this)); closeEntry(); - if (verify) { + if (doVerify) { jv = new JarVerifier(bytes); mev = new ManifestEntryVerifier(man); } - first = getNextJarEntry(); - } else { - first = e; + return (JarEntry)super.getNextEntry(); } + return e; } private byte[] getBytes(InputStream is) @@ -98,10 +108,7 @@ class JarInputStream extends ZipInputStream { { byte[] buffer = new byte[8192]; ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); - int n; - - baos.reset(); while ((n = is.read(buffer, 0, buffer.length)) != -1) { baos.write(buffer, 0, n); } @@ -133,8 +140,14 @@ class JarInputStream extends ZipInputStream { JarEntry e; if (first == null) { e = (JarEntry)super.getNextEntry(); + if (tryManifest) { + e = checkManifest(e); + tryManifest = false; + } } else { e = first; + if (first.getName().equalsIgnoreCase(JarIndex.INDEX_NAME)) + tryManifest = true; first = null; } if (jv != null && e != null) { diff --git a/src/share/classes/sun/security/pkcs11/P11Cipher.java b/src/share/classes/sun/security/pkcs11/P11Cipher.java index 1ea39e799d1ad9c786c5befcd151ee92e321bf3f..5568887a5f61a40f80c1774a5fbf7e2a49c7b8b4 100644 --- a/src/share/classes/sun/security/pkcs11/P11Cipher.java +++ b/src/share/classes/sun/security/pkcs11/P11Cipher.java @@ -74,7 +74,7 @@ final class P11Cipher extends CipherSpi { // DEC: return the length of trailing padding bytes given the specified // padded data int unpad(byte[] paddedData, int len) - throws BadPaddingException; + throws BadPaddingException, IllegalBlockSizeException; } private static class PKCS5Padding implements Padding { @@ -96,9 +96,10 @@ final class P11Cipher extends CipherSpi { } public int unpad(byte[] paddedData, int len) - throws BadPaddingException { - if (len < 1 || len > paddedData.length) { - throw new BadPaddingException("Invalid pad array length!"); + throws BadPaddingException, IllegalBlockSizeException { + if ((len < 1) || (len % blockSize != 0)) { + throw new IllegalBlockSizeException + ("Input length must be multiples of " + blockSize); } byte padValue = paddedData[len - 1]; if (padValue < 1 || padValue > blockSize) { diff --git a/src/share/demo/nio/zipfs/Demo.java b/src/share/demo/nio/zipfs/Demo.java index 3666dfd70de13bea14e233480db55805bb564bdc..99669d1fdf224dd6518f9a8553b51e376a75d1a0 100644 --- a/src/share/demo/nio/zipfs/Demo.java +++ b/src/share/demo/nio/zipfs/Demo.java @@ -75,9 +75,15 @@ public class Demo { // copy an external src file into zipfile // as entry dst + copyin_attrs, // + // copy an external src file into zipfile + // as entry dst, with attributes (timestamp) + copyout, // // copy zipfile entry src" out to file dst + copyout_attrs, // + zzmove, // // move entry path/dir from zfsrc to zfdst @@ -94,6 +100,9 @@ public class Demo { setmtime, // // set the lastModifiedTime of entry path + setatime, // + setctime, // + lsdir, // // list dir's direct child files/dirs @@ -135,12 +144,14 @@ public class Demo { attrs2, // // test different ways to print attrs + + prof, } public static void main(String[] args) throws Throwable { - Action action = Action.valueOf(args[0]);; - Map env = env = new HashMap(); + Action action = Action.valueOf(args[0]); + Map env = env = new HashMap<>(); if (action == Action.create) env.put("createNew", true); if (action == Action.tlist || action == Action.twalk) @@ -185,6 +196,16 @@ public class Demo { 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( URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)), @@ -206,6 +227,7 @@ public class Demo { 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()); } @@ -221,6 +243,28 @@ public class Demo { 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(); @@ -293,6 +337,7 @@ public class Demo { 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()); @@ -308,6 +353,13 @@ public class Demo { } } 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(); @@ -501,10 +553,11 @@ public class Demo { } private static void list(Path path, boolean verbose ) throws IOException { - if (verbose) - System.out.println(Attributes.readBasicFileAttributes(path).toString()); - else - System.out.printf(" %s%n", path.toString()); + 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()) { diff --git a/src/share/demo/nio/zipfs/README.txt b/src/share/demo/nio/zipfs/README.txt index 227a67e270e36b4a46bc0e6400a9e039da35f67e..d5517bf7baeb481e4c601863944fee63c733421e 100644 --- a/src/share/demo/nio/zipfs/README.txt +++ b/src/share/demo/nio/zipfs/README.txt @@ -2,7 +2,7 @@ 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 /demo/nio/ZipFileSystem/zipfs.jar +directory or else add /demo/nio/zipfs/zipfs.jar to your class path. The factory methods defined by the java.nio.file.FileSystems class can be @@ -10,8 +10,8 @@ used to create a FileSystem, eg: // use file type detection Map env = Collections.emptyMap(); - Path jarfile = Path.get("foo.jar"); - FileSystem fs = FileSystems.newFileSystem(jarfile, env); + Path jarfile = Paths.get("foo.jar"); + FileSystem fs = FileSystems.newFileSystem(jarfile, env, null); -or diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/JarFileSystemProvider.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/JarFileSystemProvider.java index 2fc9c83ef14d5f087ae85d289a9f152f70fc421f..dddf69e4deefd7b6edcc934fc484dbe12e44379c 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/JarFileSystemProvider.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/JarFileSystemProvider.java @@ -68,4 +68,21 @@ public class JarFileSystemProvider extends ZipFileSystemProvider 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"); + } } diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipConstants.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipConstants.java index aa7073ac15e16d9ae71ac2647c34be0af635ca40..24c28cfe80c5477b59e761892780b0c2344de47d 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipConstants.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipConstants.java @@ -31,7 +31,6 @@ package com.sun.nio.zipfs; -import java.nio.ByteBuffer; /** * @@ -48,6 +47,7 @@ class ZipConstants { 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 @@ -168,7 +168,8 @@ class ZipConstants { 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 @@ -226,34 +227,23 @@ class ZipConstants { 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 - ////////////////////////////////////////// - static final int CH(ByteBuffer b, int pos) { - return b.get(pos) & 0xff; - } - static final int SH(ByteBuffer b, int pos) { - return b.getShort(pos) & 0xffff; - } - static final long LG(ByteBuffer b, int pos) { - return b.getInt(pos) & 0xffffffffL; - } - - // central directory header (END) fields - static final long CENSIG(ByteBuffer b, int pos) { return LG(b, pos + 0); } - static final int CENVEM(ByteBuffer b, int pos) { return SH(b, pos + 4); } - static final int CENVER(ByteBuffer b, int pos) { return SH(b, pos + 6); } - static final int CENFLG(ByteBuffer b, int pos) { return SH(b, pos + 8); } - static final int CENHOW(ByteBuffer b, int pos) { return SH(b, pos + 10);} - static final long CENTIM(ByteBuffer b, int pos) { return LG(b, pos + 12);} - static final long CENCRC(ByteBuffer b, int pos) { return LG(b, pos + 16);} - static final long CENSIZ(ByteBuffer b, int pos) { return LG(b, pos + 20);} - static final long CENLEN(ByteBuffer b, int pos) { return LG(b, pos + 24);} - static final int CENNAM(ByteBuffer b, int pos) { return SH(b, pos + 28);} - static final int CENEXT(ByteBuffer b, int pos) { return SH(b, pos + 30);} - static final int CENCOM(ByteBuffer b, int pos) { return SH(b, pos + 32);} - static final int CENDSK(ByteBuffer b, int pos) { return SH(b, pos + 34);} - static final int CENATT(ByteBuffer b, int pos) { return SH(b, pos + 36);} - static final long CENATX(ByteBuffer b, int pos) { return LG(b, pos + 38);} - static final long CENOFF(ByteBuffer b, int pos) { return LG(b, pos + 42);} + // 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; diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipDirectoryStream.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipDirectoryStream.java index 3052b34e59a6633edcca78d49c7bf7dbedbf8e38..10f6bd477a9d66c8aa38cc14dccbd7da25a20310 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipDirectoryStream.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipDirectoryStream.java @@ -38,7 +38,6 @@ import java.nio.file.Path; import java.util.Iterator; import java.util.NoSuchElementException; import java.io.IOException; -import static com.sun.nio.zipfs.ZipUtils.*; /** * @@ -77,7 +76,7 @@ public class ZipDirectoryStream implements DirectoryStream { } catch (IOException e) { throw new IllegalStateException(e); } - return new Iterator() { + return new Iterator<>() { private Path next; @Override public boolean hasNext() { diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributeView.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributeView.java index d4a9a67c9fc03293ba8e540a35846f0a3af326a9..5c31ebe7ba63c3975916ecd1d3c68eee590a09bd 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributeView.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributeView.java @@ -32,7 +32,6 @@ package com.sun.nio.zipfs; -import java.nio.file.ReadOnlyFileSystemException; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.FileAttributeView; import java.nio.file.attribute.FileTime; @@ -113,6 +112,10 @@ public class ZipFileAttributeView implements BasicFileAttributeView 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 + diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributes.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributes.java index 157f9eb163ad92c5743a148746c45ec0475b2e87..687f92644c4de283a4b96ba0bcefaa1793c48df4 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributes.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileAttributes.java @@ -56,7 +56,7 @@ public class ZipFileAttributes implements BasicFileAttributes @Override public FileTime creationTime() { if (e.ctime != -1) - return FileTime.fromMillis(dosToJavaTime(e.ctime)); + return FileTime.fromMillis(e.ctime); return null; } @@ -78,13 +78,13 @@ public class ZipFileAttributes implements BasicFileAttributes @Override public FileTime lastAccessTime() { if (e.atime != -1) - return FileTime.fromMillis(dosToJavaTime(e.atime)); + return FileTime.fromMillis(e.atime); return null; } @Override public FileTime lastModifiedTime() { - return FileTime.fromMillis(dosToJavaTime(e.mtime)); + return FileTime.fromMillis(e.mtime); } @Override @@ -103,10 +103,6 @@ public class ZipFileAttributes implements BasicFileAttributes } ///////// zip entry attributes /////////// - public byte[] name() { - return Arrays.copyOf(e.name, e.name.length); - } - public long compressedSize() { return e.csize; } @@ -132,10 +128,13 @@ public class ZipFileAttributes implements BasicFileAttributes } public String toString() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(1024); Formatter fm = new Formatter(sb); - fm.format("[/%s]%n", new String(e.name)); // TBD encoding - fm.format(" creationTime : %s%n", creationTime()); + 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 diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystem.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystem.java index 152ca957c1d3ed0788e8a8b080d2f8e5f2d765e1..eddc5ac938229cfb040d3de799f6e9e6fdf934fa 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystem.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystem.java @@ -35,19 +35,18 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.nio.file.spi.*; -import java.net.URI; 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; @@ -76,8 +75,6 @@ public class ZipFileSystem extends FileSystem { private final Path zfpath; private final ZipCoder zc; - private final Object lock = new Object(); - // configurable by env map private final String defaultDir; // default dir for the file system private final String nameEncoding; // default encoding for name/comment @@ -85,6 +82,8 @@ public class ZipFileSystem extends FileSystem { 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, @@ -92,13 +91,13 @@ public class ZipFileSystem extends FileSystem { throws IOException { // configurable env setup - this.buildDirTree = TRUE.equals(env.get("buildDirTree")); - this.useTempFile = TRUE.equals(env.get("useTempFile")); - this.createNew = TRUE.equals(env.get("createNew")); + 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") : "/"; + this.defaultDir = env.containsKey("default.dir") ? + (String)env.get("default.dir") : "/"; if (this.defaultDir.charAt(0) != '/') throw new IllegalArgumentException("default dir should be absolute"); @@ -121,7 +120,8 @@ public class ZipFileSystem extends FileSystem { } this.zc = ZipCoder.get(nameEncoding); this.defaultdir = new ZipPath(this, getBytes(defaultDir)); - initZipFile(); + this.ch = zfpath.newByteChannel(READ); + this.cen = initCEN(); } @Override @@ -183,7 +183,7 @@ public class ZipFileSystem extends FileSystem { @Override public Iterable getFileStores() { - ArrayList list = new ArrayList(1); + ArrayList list = new ArrayList<>(1); list.add(new ZipFileStore(new ZipPath(this, new byte[]{'/'}))); return list; } @@ -240,19 +240,27 @@ public class ZipFileSystem extends FileSystem { @Override public void close() throws IOException { - synchronized (lock) { + beginWrite(); + try { if (!isOpen) return; - isOpen = false; - if (!streams.isEmpty()) { - synchronized(streams) { - for (InputStream is: streams) - is.close(); - } - } + isOpen = false; // set closed + } finally { + endWrite(); + } + if (!streams.isEmpty()) { // unlock and close all remaining streams + Set copy = new HashSet<>(streams); + for (InputStream is: copy) + is.close(); + } + beginWrite(); // lock and sync + try { sync(); - ch.close(); + 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(); @@ -261,97 +269,101 @@ public class ZipFileSystem extends FileSystem { for (Deflater def : deflaters) def.end(); } - for (Path p: tmppaths) { - try { - p.deleteIfExists(); - } catch (IOException x) { - x.printStackTrace(); + + synchronized (tmppaths) { + for (Path p: tmppaths) { + try { + p.deleteIfExists(); + } catch (IOException x) { + x.printStackTrace(); + } } } provider.removeFileSystem(zfpath); } - ZipFileAttributes[] getAllAttributes() throws IOException { - ensureOpen(); - int n = inodes.size(); - ZipFileAttributes[] zes = new ZipFileAttributes[n]; - Iterator itr = inodes.values().iterator(); - int i = 0; - while(itr.hasNext()) { - zes[i++] = new ZipFileAttributes(Entry.readCEN(cen, itr.next().pos)); - } - return zes; - } - - EntryName[] getEntryNames() throws IOException { - ensureOpen(); - return inodes.keySet().toArray(new EntryName[0]); - } - ZipFileAttributes getFileAttributes(byte[] path) throws IOException { - synchronized (lock) { - Entry e = getEntry0(path); - if (e == null) { - if (path.length == 0) { - e = new Entry(new byte[0]); // root - } else if (buildDirTree) { - IndexNode inode = getDirs().get(new EntryName(path)); - if (inode == null) - return null; - e = new Entry(inode.name); - } else { + 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.method = METHOD_STORED; // STORED for dir - BasicFileAttributes bfas = Attributes.readBasicFileAttributes(zfpath); - if (bfas.lastModifiedTime() != null) - e.mtime = javaToDosTime(bfas.lastModifiedTime().toMillis()); - if (bfas.lastAccessTime() != null) - e.atime = javaToDosTime(bfas.lastAccessTime().toMillis()); - if (bfas.creationTime() != null) - e.ctime = javaToDosTime(bfas.creationTime().toMillis()); + e = new Entry(inode.name); + } else { + return null; } - return new ZipFileAttributes(e); + 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(); - synchronized (lock) { + 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 = javaToDosTime(mtime.toMillis()); + e.mtime = mtime.toMillis(); if (atime != null) - e.atime = javaToDosTime(atime.toMillis()); + e.atime = atime.toMillis(); if (ctime != null) - e.ctime = javaToDosTime(ctime.toMillis()); + e.ctime = ctime.toMillis(); update(e); + } finally { + endWrite(); } } boolean exists(byte[] path) throws IOException { - return getEntry0(path) != null; + beginRead(); + try { + ensureOpen(); + return getEntry0(path) != null; + } finally { + endRead(); + } } boolean isDirectory(byte[] path) throws IOException { - synchronized (lock) { - if (buildDirTree) { - return getDirs().containsKey(new EntryName(path)); - } + if (buildDirTree) + return getDirs().containsKey(IndexNode.keyOf(path)); + + beginRead(); + try { Entry e = getEntry0(path); return (e != null && e.isDir()) || path.length == 0; + } finally { + endRead(); } } @@ -368,12 +380,14 @@ public class ZipFileSystem extends FileSystem { DirectoryStream.Filter filter) throws IOException { - synchronized (lock) { + beginWrite(); // iteration of inodes needs exclusive lock + try { + ensureOpen(); if (buildDirTree) { - IndexNode inode = getDirs().get(new EntryName(path)); + IndexNode inode = getDirs().get(IndexNode.keyOf(path)); if (inode == null) throw new NotDirectoryException(getString(path)); - List list = new ArrayList(); + List list = new ArrayList<>(); IndexNode child = inode.child; while (child != null) { ZipPath zp = toZipPath(child.name); @@ -386,25 +400,26 @@ public class ZipFileSystem extends FileSystem { if (!isDirectory(path)) throw new NotDirectoryException(getString(path)); - List list = new ArrayList(); - EntryName[] entries = getEntryNames(); + List list = new ArrayList<>(); path = toDirectoryPath(path); - for (EntryName en :entries) { - if (!isParentOf(path, en.name)) // is "path" the parent of "name" + for (IndexNode key : inodes.keySet()) { + if (!isParentOf(path, key.name)) // is "path" the parent of "name" continue; int off = path.length; - while (off < en.name.length) { - if (en.name[off] == '/') + while (off < key.name.length) { + if (key.name[off] == '/') break; off++; } - if (off < (en.name.length - 1)) + if (off < (key.name.length - 1)) continue; - ZipPath zp = toZipPath(en.name); + ZipPath zp = toZipPath(key.name); if (filter == null || filter.accept(zp)) list.add(zp); } return list.iterator(); + } finally { + endWrite(); } } @@ -413,16 +428,18 @@ public class ZipFileSystem extends FileSystem { { checkWritable(); dir = toDirectoryPath(dir); - synchronized (lock) { + beginWrite(); + try { ensureOpen(); - // pseudo root dir, or exiting dir - if (dir.length == 0 || exists(dir)) + if (dir.length == 0 || exists(dir)) // root dir, or exiting dir throw new FileAlreadyExistsException(getString(dir)); - checkParents(dir); + checkParents(dir); Entry e = new Entry(dir, Entry.NEW); - e.method = METHOD_STORED; // STORED for dir + e.method = METHOD_STORED; // STORED for dir update(e); + } finally { + endWrite(); } } @@ -432,7 +449,10 @@ public class ZipFileSystem extends FileSystem { checkWritable(); if (Arrays.equals(src, dst)) return; // do nothing, src and dst are the same - synchronized (lock) { + + beginWrite(); + try { + ensureOpen(); Entry eSrc = getEntry0(src); // ensureOpen checked if (eSrc == null) throw new NoSuchFileException(getString(src)); @@ -457,11 +477,6 @@ public class ZipFileSystem extends FileSystem { } Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry u.name = dst; // change name - // don't touch the "nlen and elen" here. writeLOC() always - // re-calculate from "name" and "extra" for the correct length, - // copyLOCEntry however needs the original lengths to skip the - // loc header. - // u.nlen = dst.length; if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) { u.type = eSrc.type; // make it the same type @@ -475,10 +490,12 @@ public class ZipFileSystem extends FileSystem { } } if (!hasCopyAttrs) - u.mtime = u.atime= u.ctime = javaToDosTime(System.currentTimeMillis()); + u.mtime = u.atime= u.ctime = System.currentTimeMillis(); update(u); if (deletesrc) updateDelete(eSrc); + } finally { + endWrite(); } } @@ -501,7 +518,9 @@ public class ZipFileSystem extends FileSystem { if (opt == APPEND) hasAppend = true; } - synchronized (lock) { + 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) @@ -512,27 +531,33 @@ public class ZipFileSystem extends FileSystem { copyStream(is, os); is.close(); return os; - } - return getOutputStream(new Entry(e, Entry.NEW)); + } + 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 { - synchronized (lock) { + 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(); } } @@ -559,78 +584,111 @@ public class ZipFileSystem extends FileSystem { if (options.contains(StandardOpenOption.WRITE) || options.contains(StandardOpenOption.APPEND)) { checkWritable(); - 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(); + 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 { - 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(); - } - }; + 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(); + } } } @@ -647,125 +705,131 @@ public class ZipFileSystem extends FileSystem { checkOptions(options); final boolean forWrite = (options.contains(StandardOpenOption.WRITE) || options.contains(StandardOpenOption.APPEND)); - Entry e = getEntry0(path); - if (forWrite) { - checkWritable(); - if (e == null) { + 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 = javaToDosTime(System.currentTimeMillis()); - u.size = Attributes.readBasicFileAttributes(u.file).size(); - update(u); } else { - if (!isFCH) // if this is a new fch for reading - removeTempPathForEntry(tmpfile); + 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 @@ -776,11 +840,9 @@ public class ZipFileSystem extends FileSystem { // input streams are all closed by the obtainers. private Set exChClosers = new HashSet<>(); - private Set tmppaths = new HashSet<>(); + private Set tmppaths = Collections.synchronizedSet(new HashSet()); private Path getTempPathForEntry(byte[] path) throws IOException { Path tmpPath = createTempFileInSameDirectoryAs(zfpath); - tmppaths.add(tmpPath); - if (path != null) { Entry e = getEntry0(path); if (e != null) { @@ -805,9 +867,14 @@ public class ZipFileSystem extends FileSystem { // check if all parents really exit. ZIP spec does not require // the existence of any "parent directory". private void checkParents(byte[] path) throws IOException { - while ((path = getParent(path)) != null) { - if (!inodes.containsKey(new EntryName(path))) - throw new NoSuchFileException(getString(path)); + beginRead(); + try { + while ((path = getParent(path)) != null) { + if (!inodes.containsKey(IndexNode.keyOf(path))) + throw new NoSuchFileException(getString(path)); + } + } finally { + endRead(); } } @@ -839,25 +906,40 @@ public class ZipFileSystem extends FileSystem { return true; } - /////////////////////////////////////////////////////////////////// - private void initZipFile() throws IOException { - ch = zfpath.newByteChannel(READ); - initCEN(); + 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 SeekableByteChannel ch; // channel to the zipfile - ByteBuffer cen; // CEN & ENDHDR + 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) - // name -> pos (in cen), package private for ZipInfo - LinkedHashMap inodes; + private final ReadWriteLock rwlock = new ReentrantReadWriteLock(); + + // name -> pos (in cen), IndexNode itself can be used as a "key" + private LinkedHashMap inodes; - byte[] getBytes(String name) { + final byte[] getBytes(String name) { return zc.getBytes(name); } - String getString(byte[] name) { + + final String getString(byte[] name) { return zc.toString(name); } @@ -881,7 +963,7 @@ public class ZipFileSystem extends FileSystem { // 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). - private long readFullyAt(byte[] buf, int off, long len, long pos) + final long readFullyAt(byte[] buf, int off, long len, long pos) throws IOException { ByteBuffer bb = ByteBuffer.wrap(buf); @@ -890,7 +972,7 @@ public class ZipFileSystem extends FileSystem { return readFullyAt(bb, pos); } - private long readFullyAt(ByteBuffer bb, long pos) + private final long readFullyAt(ByteBuffer bb, long pos) throws IOException { synchronized(ch) { @@ -971,12 +1053,12 @@ public class ZipFileSystem extends FileSystem { // 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 long initCEN() throws IOException { + private byte[] initCEN() throws IOException { end = findEND(); if (end.endpos == 0) { - inodes = new LinkedHashMap(10); + inodes = new LinkedHashMap<>(10); locpos = 0; - return 0; // only END header present + return null; // only END header present } if (end.cenlen > end.endpos) zerror("invalid END header (bad central directory size)"); @@ -989,18 +1071,14 @@ public class ZipFileSystem extends FileSystem { zerror("invalid END header (bad central directory offset)"); // read in the CEN and END - cen = ByteBuffer.allocate((int)(end.cenlen + ENDHDR)); - if (readFullyAt(cen, cenpos) != end.cenlen + ENDHDR) { + byte[] cen = new byte[(int)(end.cenlen + ENDHDR)]; + if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { zerror("read CEN tables failed"); } - cen.order(ByteOrder.LITTLE_ENDIAN).flip(); - // Iterate through the entries in the central directory - inodes = new LinkedHashMap(end.centot + 1); + inodes = new LinkedHashMap<>(end.centot + 1); int pos = 0; - int limit = cen.remaining() - ENDHDR; - int i = 0; - byte[] bBuf = new byte[1024]; + int limit = cen.length - ENDHDR; while (pos < limit) { if (CENSIG(cen, pos) != CENSIG) zerror("invalid CEN header (bad signature)"); @@ -1011,24 +1089,19 @@ public class ZipFileSystem extends FileSystem { if ((CENFLG(cen, pos) & 1) != 0) zerror("invalid CEN header (encrypted entry)"); if (method != METHOD_STORED && method != METHOD_DEFLATED) - zerror("invalid CEN header (bad compression method: " + method + ")"); + zerror("invalid CEN header (unsupported compression method: " + method + ")"); if (pos + CENHDR + nlen > limit) zerror("invalid CEN header (bad header size)"); - if (bBuf.length < nlen) - bBuf = new byte[nlen]; - cen.position(pos + CENHDR); - byte[] name = new byte[nlen]; - cen.get(name); - inodes.put(new EntryName(name), new IndexNode(name, pos)); + byte[] name = Arrays.copyOfRange(cen, pos + CENHDR, pos + CENHDR + nlen); + IndexNode inode = new IndexNode(name, pos); + inodes.put(inode, inode); // skip ext and comment - cen.position(pos += (CENHDR + nlen + elen + clen)); - i++; + pos += (CENHDR + nlen + elen + clen); } - if (cen.remaining() != ENDHDR) { + if (pos + ENDHDR != cen.length) { zerror("invalid CEN header (bad header size)"); } - dirs = null; // clear the dir map - return cenpos; + return cen; } private void ensureOpen() throws IOException { @@ -1038,27 +1111,40 @@ public class ZipFileSystem extends FileSystem { // Creates a new empty temporary file in the same directory as the // specified file. A variant of File.createTempFile. - private static Path createTempFileInSameDirectoryAs(Path path) + private Path createTempFileInSameDirectoryAs(Path path) throws IOException { Path parent = path.toAbsolutePath().getParent(); String dir = (parent == null)? "." : parent.toString(); - return File.createTempFile("zipfstmp", null, new File(dir)).toPath(); + 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) { - EntryName en = new EntryName(e.name); - inodes.remove(en); - hasUpdate = true; + beginWrite(); + try { + inodes.remove(IndexNode.keyOf(e.name)); //inodes.remove(e.name); + hasUpdate = true; + dirs = null; + } finally { + endWrite(); + } } private void update(Entry e) { - EntryName en = new EntryName(e.name); - inodes.put(en, e); - hasUpdate = true; + 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 @@ -1080,13 +1166,18 @@ public class ZipFileSystem extends FileSystem { else size = 16; } - if (updateHeader) { // if we need update the loc header - locoff += LOCHDR + e.nlen + e.elen; // skip header + // 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 { - size += LOCHDR + e.nlen + e.elen + e.csize; - written = size; + 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 && @@ -1103,7 +1194,7 @@ public class ZipFileSystem extends FileSystem { // sync the zip file system, if there is any udpate private void sync() throws IOException { - assert Thread.holdsLock(this); + //System.out.printf("->sync(%s) starting....!%n", toString()); // check ex-closer if (!exChClosers.isEmpty()) { @@ -1117,7 +1208,6 @@ public class ZipFileSystem extends FileSystem { } if (!hasUpdate) return; - Path tmpFile = createTempFileInSameDirectoryAs(zfpath); OutputStream os = tmpFile.newOutputStream(WRITE); ArrayList elist = new ArrayList<>(inodes.size()); @@ -1174,7 +1264,7 @@ public class ZipFileSystem extends FileSystem { x.printStackTrace(); // skip any in-accurate entry } } else { // unchanged inode - e = Entry.readCEN(cen, inode.pos); + e = Entry.readCEN(this, inode.pos); try { written += copyLOCEntry(e, false, os, written, buf); elist.add(e); @@ -1195,6 +1285,11 @@ public class ZipFileSystem extends FileSystem { 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 @@ -1209,45 +1304,41 @@ public class ZipFileSystem extends FileSystem { ch.close(); zfpath.delete(); } + tmpFile.moveTo(zfpath, REPLACE_EXISTING); hasUpdate = false; // clear - + /* if (isOpen) { ch = zfpath.newByteChannel(READ); // re-fresh "ch" and "cen" - initCEN(); + cen = initCEN(); } - //System.out.println("->sync() done!"); + */ + //System.out.printf("->sync(%s) done!%n", toString()); } private Entry getEntry0(byte[] path) throws IOException { - assert Thread.holdsLock(this); - if (path == null) throw new NullPointerException("path"); if (path.length == 0) return null; - EntryName en = new EntryName(path); IndexNode inode = null; - synchronized (lock) { - ensureOpen(); - if ((inode = inodes.get(en)) == null) { - if (path[path.length -1] == '/') // already has a slash - return null; - path = Arrays.copyOf(path, path.length + 1); - path[path.length - 1] = '/'; - en.name(path); - if ((inode = inodes.get(en)) == null) - return null; - } - if (inode instanceof Entry) - return (Entry)inode; - return Entry.readCEN(cen, inode.pos); + 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 entry : inodes.entrySet()) { + for (Map.Entry entry : inodes.entrySet()) { byte[] ename = entry.getKey().name; if (isParentOf(name, ename)) return true; @@ -1259,18 +1350,16 @@ public class ZipFileSystem extends FileSystem { throws IOException { checkWritable(); - synchronized(lock) { - 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); - } + 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); } } @@ -1289,9 +1378,8 @@ public class ZipFileSystem extends FileSystem { // (2) updating/replacing the contents of the specified existing entry. private OutputStream getOutputStream(Entry e) throws IOException { - ensureOpen(); if (e.mtime == -1) - e.mtime = javaToDosTime(System.currentTimeMillis()); + 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 @@ -1334,7 +1422,7 @@ public class ZipFileSystem extends FileSystem { long bufSize = e.size + 2; // Inflater likes a bit of slack if (bufSize > 65536) bufSize = 8192; - final long size = e.size;; + final long size = e.size; eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) { private boolean isClosed = false; @@ -1343,6 +1431,7 @@ public class ZipFileSystem extends FileSystem { releaseInflater(inf); this.in.close(); isClosed = true; + streams.remove(this); } } // Override fill() method to provide an extra "dummy" byte @@ -1372,7 +1461,9 @@ public class ZipFileSystem extends FileSystem { Integer.MAX_VALUE : (int) avail; } }; - } else if (e.method != METHOD_STORED) { + } else if (e.method == METHOD_STORED) { + // TBD: wrap/ it does not seem necessary + } else { throw new ZipException("invalid compression method"); } streams.add(eis); @@ -1382,11 +1473,11 @@ public class ZipFileSystem extends FileSystem { // Inner class implementing the input stream used to read // a (possibly compressed) zip file entry. private class EntryInputStream extends InputStream { - private SeekableByteChannel zfch; // local ref to zipfs's "ch". zipfs.ch might + 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 long size; // uncompressed size of this entry + protected final long size; // uncompressed size of this entry EntryInputStream(Entry e, SeekableByteChannel zfch) throws IOException @@ -1527,14 +1618,14 @@ public class ZipFileSystem extends FileSystem { } } - private static void zerror(String msg) { + 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 List inflaters = new ArrayList<>(); + private final List inflaters = new ArrayList<>(); // Gets an inflater from the list of available inflaters or allocates // a new one. @@ -1563,7 +1654,7 @@ public class ZipFileSystem extends FileSystem { } // List of available Deflater objects for compression - private List deflaters = new ArrayList<>(); + private final List deflaters = new ArrayList<>(); // Gets an deflater from the list of available deflaters or allocates // a new one. @@ -1660,44 +1751,40 @@ public class ZipFileSystem extends FileSystem { } } - // wrapper for the byte[] name - static class EntryName { + // 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 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; + } - public EntryName (byte[] name) { - name(name); + final static IndexNode keyOf(byte[] name) { // get a lookup key; + return new IndexNode(name, -1); } - void name(byte[] name) { - this.name = name; + 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 EntryName)) + if (!(other instanceof IndexNode)) return false; - return Arrays.equals(name, ((EntryName)other).name); + return Arrays.equals(name, ((IndexNode)other).name); } public int hashCode() { return hashcode; } - } - - // can simply use Integer instead, if we don't use it to - // build a internal node tree. - static class IndexNode { - byte[] name; - int pos = -1; // postion in cen table, -1 menas the - // entry does not exists in zip file - IndexNode(byte[] name, int pos) { - this.name = name; - this.pos = pos; - } IndexNode() {} - IndexNode sibling; IndexNode child; // 1st child } @@ -1723,37 +1810,25 @@ public class ZipFileSystem extends FileSystem { long crc = -1; // crc-32 of entry data long csize = -1; // compressed size of entry data long size = -1; // uncompressed size of entry data - int nlen; - int elen; byte[] extra; - // loc - long startPos; - long endPos; // exclusive - // cen int versionMade; int disk; int attrs; long attrsEx; long locoff; - - int clen; byte[] comment; - // ZIP64 flag - boolean hasZip64; - Entry() {} Entry(byte[] name) { - this.name = name; - //this.nlen = name.length; - this.mtime = javaToDosTime(System.currentTimeMillis()); - this.crc = 0; - this.size = 0; - this.csize = 0; - this.method = METHOD_DEFLATED; + 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) { @@ -1761,16 +1836,9 @@ public class ZipFileSystem extends FileSystem { this.type = type; } - Entry (byte[] name, Path file, int type) { - this(name, type); - this.file = file; - this.method = METHOD_STORED; - } - - Entry(Entry e) { + Entry (Entry e, int type) { this.version = e.version; - this.name = e.name; // copyOf? - this.nlen = e.nlen; + this.name = e.name; this.ctime = e.ctime; this.atime = e.atime; this.mtime = e.mtime; @@ -1778,25 +1846,21 @@ public class ZipFileSystem extends FileSystem { this.size = e.size; this.csize = e.csize; this.method = e.method; - this.extra = (e.extra == null)? - null:Arrays.copyOf(e.extra, e.extra.length); - this.elen = e.elen; + 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.clen = e.clen; - this.comment = (e.comment == null)? - null:Arrays.copyOf(e.comment, e.comment.length); - this.startPos = e.startPos; - this.endPos = e.endPos; - this.hasZip64 = e.hasZip64;; + this.comment = e.comment; + + this.type = type; } - Entry (Entry e, int type) { - this(e); - this.type = type; + Entry (byte[] name, Path file, int type) { + this(name, type); + this.file = file; + this.method = METHOD_STORED; } boolean isDir() { @@ -1814,77 +1878,45 @@ public class ZipFileSystem extends FileSystem { } ///////////////////// CEN ////////////////////// - static Entry readCEN(ByteBuffer cen, int pos) throws IOException + static Entry readCEN(ZipFileSystem zipfs, int pos) + throws IOException { - return new Entry().cen(cen, pos); + return new Entry().cen(zipfs, pos); } - private Entry cen(ByteBuffer cen, int pos) throws IOException + 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 = CENTIM(cen, pos); + mtime = dosToJavaTime(CENTIM(cen, pos)); crc = CENCRC(cen, pos); csize = CENSIZ(cen, pos); size = CENLEN(cen, pos); - nlen = CENNAM(cen, pos); - elen = CENEXT(cen, pos); - clen = CENCOM(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); - cen.position(pos + CENHDR); - name = new byte[nlen]; - cen.get(name); + pos += CENHDR; + name = Arrays.copyOfRange(cen, pos, pos + nlen); + pos += nlen; if (elen > 0) { - extra = new byte[elen]; - cen.get(extra); - if (csize == ZIP64_MINVAL || size == ZIP64_MINVAL || - locoff == ZIP64_MINVAL) { - int off = 0; - while (off + 4 < elen) { - // extra spec: HeaderID+DataSize+Data - int sz = SH(extra, off + 2); - if (SH(extra, off) == EXTID_ZIP64) { - off += 4; - if (size == ZIP64_MINVAL) { - // if invalid zip64 extra fields, just skip - if (sz < 8 || (off + 8) > elen) - break; - size = LL(extra, off); - sz -= 8; - off += 8; - } - if (csize == ZIP64_MINVAL) { - if (sz < 8 || (off + 8) > elen) - break; - csize = LL(extra, off); - sz -= 8; - off += 8; - } - if (locoff == ZIP64_MINVAL) { - if (sz < 8 || (off + 8) > elen) - break; - locoff = LL(extra, off); - sz -= 8; - off += 8; - } - break; - } - off += (sz + 4); - } - } + extra = Arrays.copyOfRange(cen, pos, pos + elen); + pos += elen; + readExtra(zipfs); } if (clen > 0) { - comment = new byte[clen]; - cen.get(comment); + comment = Arrays.copyOfRange(cen, pos, pos + clen); } return this; } @@ -1897,31 +1929,37 @@ public class ZipFileSystem extends FileSystem { long csize0 = csize; long size0 = size; long locoff0 = locoff; - int e64len = 0; + 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 - nlen = (name != null) ? name.length : 0; - elen = (extra != null) ? extra.length : 0; - clen = (comment != null) ? comment.length : 0; - - boolean hasZip64 = false; + 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; - e64len += 8; // csize(8) - hasZip64 = true; + elen64 += 8; // csize(8) } if (size >= ZIP64_MINVAL) { size0 = ZIP64_MINVAL; // size(8) - e64len += 8; - hasZip64 = true; + elen64 += 8; } if (locoff >= ZIP64_MINVAL) { locoff0 = ZIP64_MINVAL; - e64len += 8; // offset(8) - hasZip64 = true; + 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 (hasZip64) { + if (elen64 != 0) { writeShort(os, 45); // ver 4.5 for zip64 writeShort(os, 45); } else { @@ -1930,18 +1968,14 @@ public class ZipFileSystem extends FileSystem { } writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method - writeInt(os, mtime); // last modification time + // 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 (hasZip64) { - // + headid(2) + datasize(2) - writeShort(os, e64len + 4 + elen); - } else { - writeShort(os, elen); - } if (comment != null) { writeShort(os, Math.min(clen, 0xffff)); } else { @@ -1952,9 +1986,9 @@ public class ZipFileSystem extends FileSystem { writeInt(os, 0); // external file attributes (unused) writeInt(os, locoff0); // relative offset of local header writeBytes(os, name); - if (hasZip64) { + if (elen64 != 0) { writeShort(os, EXTID_ZIP64);// Zip64 extra - writeShort(os, e64len); + writeShort(os, elen64); // size of "this" extra block if (size0 == ZIP64_MINVAL) writeLong(os, size); if (csize0 == ZIP64_MINVAL) @@ -1962,58 +1996,73 @@ public class ZipFileSystem extends FileSystem { if (locoff0 == ZIP64_MINVAL) writeLong(os, locoff); } - if (extra != null) { - writeBytes(os, extra); + 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 (comment != null) { - //TBD: 0, Math.min(commentBytes.length, 0xffff)); + 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 + (hasZip64?(e64len + 4):0); + return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT; } ///////////////////// LOC ////////////////////// - static Entry readLOC(ZipFileSystem zf, long pos) + static Entry readLOC(ZipFileSystem zipfs, long pos) throws IOException { - return readLOC(zf, pos, new byte[1024]); + return readLOC(zipfs, pos, new byte[1024]); } - static Entry readLOC(ZipFileSystem zf, long pos, byte[] buf) + static Entry readLOC(ZipFileSystem zipfs, long pos, byte[] buf) throws IOException { - return new Entry().loc(zf, pos, buf); + return new Entry().loc(zipfs, pos, buf); } - Entry loc(ZipFileSystem zf, long pos, byte[] buf) + Entry loc(ZipFileSystem zipfs, long pos, byte[] buf) throws IOException { assert (buf.length >= LOCHDR); - if (zf.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR) { + if (zipfs.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR) throw new ZipException("loc: reading failed"); - } - if (LOCSIG(buf) != LOCSIG) { + if (LOCSIG(buf) != LOCSIG) throw new ZipException("loc: wrong sig ->" + Long.toString(LOCSIG(buf), 16)); - } - startPos = pos; + //startPos = pos; version = LOCVER(buf); flag = LOCFLG(buf); method = LOCHOW(buf); - mtime = LOCTIM(buf); + mtime = dosToJavaTime(LOCTIM(buf)); crc = LOCCRC(buf); csize = LOCSIZ(buf); size = LOCLEN(buf); - nlen = LOCNAM(buf); - elen = LOCEXT(buf); + int nlen = LOCNAM(buf); + int elen = LOCEXT(buf); name = new byte[nlen]; - if (zf.readFullyAt(name, 0, nlen, pos + LOCHDR) != 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 (zf.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen) + if (zipfs.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen) != elen) { throw new ZipException("loc: ext reading failed"); } @@ -2021,7 +2070,7 @@ public class ZipFileSystem extends FileSystem { pos += (LOCHDR + nlen + elen); if ((flag & FLAG_DATADESCR) != 0) { // Data Descriptor - Entry e = zf.getEntry0(name); // get the size/csize from cen + 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; @@ -2032,7 +2081,6 @@ public class ZipFileSystem extends FileSystem { else pos += 16; } else { - boolean hasZip64 = false; if (extra != null && (size == ZIP64_MINVAL || csize == ZIP64_MINVAL)) { // zip64 ext: must include both size and csize @@ -2042,7 +2090,6 @@ public class ZipFileSystem extends FileSystem { if (SH(extra, off) == EXTID_ZIP64 && sz == 16) { size = LL(extra, off + 4); csize = LL(extra, off + 12); - hasZip64 = true; break; } off += (sz + 4); @@ -2050,7 +2097,6 @@ public class ZipFileSystem extends FileSystem { } pos += (method == METHOD_STORED ? size : csize); } - endPos = pos; return this; } @@ -2058,14 +2104,18 @@ public class ZipFileSystem extends FileSystem { 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 - writeInt(os, mtime); // last modification time - + // 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); @@ -2073,7 +2123,7 @@ public class ZipFileSystem extends FileSystem { writeInt(os, 0); } else { if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { - hasZip64 = true; + 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 @@ -2082,29 +2132,45 @@ public class ZipFileSystem extends FileSystem { writeShort(os, method); // compression method writeInt(os, mtime); // last modification time writeInt(os, crc); // crc-32 - if (hasZip64) { + if (elen64 != 0) { writeInt(os, ZIP64_MINVAL); writeInt(os, ZIP64_MINVAL); - //TBD: e.elen += 20; //headid(2) + size(2) + size(8) + csize(8) } 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 + (hasZip64 ? 20 : 0)); + writeShort(os, elen + elen64 + elenEXTT); writeBytes(os, name); - if (hasZip64) { - // TBD: should we update extra directory? + 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 + (hasZip64 ? 20 : 0); + return LOCHDR + name.length + elen + elen64 + elenEXTT; } // Data Descriptior @@ -2125,17 +2191,18 @@ public class ZipFileSystem extends FileSystem { } // read NTFS, UNIX and ZIP64 data from cen.extra - void readExtra() { + 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 sz = SH(extra, off + 2); - int tag = SH(extra, off); - off += 4; 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) { @@ -2165,18 +2232,66 @@ public class ZipFileSystem extends FileSystem { break; if (SH(extra, pos + 2) != 24) break; - mtime = LL(extra, pos + 4); - atime = LL(extra, pos + 12); - ctime = LL(extra, pos + 20); + // 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_UNIX: - atime = LG(extra, pos); - mtime = LG(extra, pos + 4); + 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: // unknow + default: // unknown tag + System.arraycopy(extra, off, extra, newOff, sz + 4); + newOff += (sz + 4); } - off += sz; + off += (sz + 4); } + if (newOff != 0 && newOff != extra.length) + extra = Arrays.copyOf(extra, newOff); + else + extra = null; } } @@ -2201,9 +2316,9 @@ public class ZipFileSystem extends FileSystem { // structure. // A possible solution is to build the node tree ourself as // implemented below. - private HashMap dirs; + private HashMap dirs; private IndexNode root; - private IndexNode addToDir(EntryName child) { + private IndexNode addToDir(IndexNode child) { IndexNode cinode = dirs.get(child); if (cinode != null) return cinode; @@ -2213,7 +2328,7 @@ public class ZipFileSystem extends FileSystem { IndexNode pinode; if (pname != null) - pinode = addToDir(new EntryName(pname)); + pinode = addToDir(IndexNode.keyOf(pname)); else pinode = root; cinode = inodes.get(child); @@ -2222,8 +2337,8 @@ public class ZipFileSystem extends FileSystem { pinode.child = cinode; return null; } - cinode = dirs.get(child); - if (cinode == null) // pseudo directry entry + //cinode = dirs.get(child); + if (cinode == null) // pseudo directry entry cinode = new IndexNode(cname, -1); cinode.sibling = pinode.child; pinode.child = cinode; @@ -2232,26 +2347,21 @@ public class ZipFileSystem extends FileSystem { return cinode; } - private HashMap getDirs() + private HashMap getDirs() throws IOException { - if (hasUpdate) - sync(); - if (dirs != null) + 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; - dirs = new HashMap(); - byte[] empty = new byte[0]; - root = new IndexNode(empty, -1); - dirs.put(new EntryName(empty), root); - - EntryName[] names = inodes.keySet().toArray(new EntryName[0]); - int i = names.length; - while (--i >= 0) { - addToDir(names[i]); - } - // for (int i EntryName en : inodes.keySet()) { - // addToDir(en); - // } - return dirs; + } finally { + endWrite(); + } } } diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystemProvider.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystemProvider.java index a43b2cf7de34ef19439151cde7c5f426b70b9666..08c3a17648c2f0e3e2e72d8b678b940968be6923 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystemProvider.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipFileSystemProvider.java @@ -55,6 +55,8 @@ import java.util.Set; */ public class ZipFileSystemProvider extends FileSystemProvider { + + private final Map filesystems = new HashMap<>(); public ZipFileSystemProvider() {} @@ -101,10 +103,16 @@ public class ZipFileSystemProvider extends FileSystemProvider { throws IOException { synchronized(filesystems) { - if (filesystems.containsKey(path)) - throw new FileSystemAlreadyExistsException(); + Path realPath = null; + if (path.exists()) { + realPath = path.toRealPath(true); + if (filesystems.containsKey(realPath)) + throw new FileSystemAlreadyExistsException(); + } ZipFileSystem zipfs = new ZipFileSystem(this, path, env); - filesystems.put(path, zipfs); + if (realPath == null) + realPath = path.toRealPath(true); + filesystems.put(realPath, zipfs); return zipfs; } } @@ -137,16 +145,21 @@ public class ZipFileSystemProvider extends FileSystemProvider { @Override public FileSystem getFileSystem(URI uri) { synchronized (filesystems) { - ZipFileSystem zipfs = filesystems.get(uriToPath(uri)); + 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) { + void removeFileSystem(Path zfpath) throws IOException { synchronized (filesystems) { - filesystems.remove(zfpath); + filesystems.remove(zfpath.toRealPath(true)); } } } diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipInfo.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipInfo.java index 741376daf0d4c62971cf25030097f7648fcaf330..dd18cc9f4440044337b006ae815da5a29d9e709e 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipInfo.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipInfo.java @@ -31,7 +31,6 @@ package com.sun.nio.zipfs; -import java.io.PrintStream; import java.nio.file.Paths; import java.util.Collections; import java.util.Iterator; @@ -41,7 +40,7 @@ import static com.sun.nio.zipfs.ZipConstants.*; import static com.sun.nio.zipfs.ZipUtils.*; /** - * Print the loc and cen tables of the ZIP file + * Print all loc and cen headers of the ZIP file * * @author Xueming Shen */ @@ -49,34 +48,38 @@ import static com.sun.nio.zipfs.ZipUtils.*; public class ZipInfo { public static void main(String[] args) throws Throwable { - if (args.length < 2) { - print("Usage: java ZipInfo [cen|loc] zfname"); + if (args.length < 1) { + print("Usage: java ZipInfo zfname"); } else { Map env = Collections.emptyMap(); ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider() - .newFileSystem(Paths.get(args[1]), env)); - - long pos = 0; + .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); - if ("loc".equals(args[0])) { - print("[Local File Header]%n"); - byte[] buf = new byte[1024]; - for (int i = 0; i < zfs.getEntryNames().length; i++) { - Entry loc = Entry.readLOC(zfs, pos, buf); - print("--------loc[%x]--------%n", pos); - printLOC(loc); - pos = loc.endPos; - } - } if ("cen".equals(args[0])) { - int i = 0; - Iterator itr = zfs.inodes.values().iterator(); - print("[Central Directory Header]%n"); - while (itr.hasNext()) { - Entry cen = Entry.readCEN(zfs.cen, itr.next().pos); - print("--------cen[%d]--------%n", i); - printCEN(cen); - i++; + // 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(); } @@ -86,47 +89,135 @@ public class ZipInfo { System.out.printf(fmt, objs); } - static void printLOC(Entry loc) { - print(" [%x, %x]%n", loc.startPos, loc.endPos); - print(" Signature : %8x%n", LOCSIG); - print(" Version : %4x [%d.%d]%n", - loc.version, loc. version/10, loc. version%10); - print(" Flag : %4x%n", loc.flag); - print(" Method : %4x%n", loc. method); - print(" LastMTime : %8x [%tc]%n", - loc.mtime, dosToJavaTime(loc.mtime)); - print(" CRC : %8x%n", loc.crc); - print(" CSize : %8x%n", loc.csize); - print(" Size : %8x%n", loc.size); - print(" NameLength : %4x [%s]%n", - loc.nlen, new String(loc.name)); - print(" ExtraLength : %4x%n", loc.elen); - if (loc.hasZip64) - print(" *ZIP64*%n"); + 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 printCEN(Entry cen) { - print(" Signature : %08x%n", CENSIG); - print(" VerMadeby : %4x [%d.%d]%n", - cen.versionMade, cen.versionMade/10, cen.versionMade%10); - print(" VerExtract : %4x [%d.%d]%n", - cen.version, cen.version/10, cen.version%10); - print(" Flag : %4x%n", cen.flag); - print(" Method : %4x%n", cen.method); - print(" LastMTime : %8x [%tc]%n", - cen.mtime, dosToJavaTime(cen.mtime)); - print(" CRC : %8x%n", cen.crc); - print(" CSize : %8x%n", cen.csize); - print(" Size : %8x%n", cen.size); - print(" NameLen : %4x [%s]%n", - cen.nlen, new String(cen.name)); - print(" ExtraLen : %4x%n", cen.elen); - print(" CommentLen : %4x%n", cen.clen); - print(" DiskStart : %4x%n", cen.disk); - print(" Attrs : %4x%n", cen.attrs); - print(" AttrsEx : %8x%n", cen.attrsEx); - print(" LocOff : %8x%n", cen.locoff); - if (cen.hasZip64) - print(" *ZIP64*%n"); + 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; + } } } diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipPath.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipPath.java index 1ceab8fc81ad95dba5e21e91c06e3f89f4c95609..5fba2fafa9aef2c022b056e3418bd66153328b4c 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipPath.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipPath.java @@ -32,24 +32,19 @@ package com.sun.nio.zipfs; import java.io.File; -import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; -import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; import java.nio.file.*; import java.nio.file.DirectoryStream.Filter; -import java.nio.file.spi.FileSystemProvider; 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 java.util.regex.Matcher; -import java.util.regex.Pattern; import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardCopyOption.*; @@ -599,7 +594,7 @@ public class ZipPath extends Path { } private static final DirectoryStream.Filter acceptAllFilter = - new DirectoryStream.Filter() { + new DirectoryStream.Filter<>() { @Override public boolean accept(Path entry) { return true; } }; @@ -625,7 +620,7 @@ public class ZipPath extends Path { // create a matcher and return a filter that uses it. final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob); - DirectoryStream.Filter filter = new DirectoryStream.Filter() { + DirectoryStream.Filter filter = new DirectoryStream.Filter<>() { @Override public boolean accept(Path entry) { return matcher.matches(entry.getName()); @@ -758,7 +753,7 @@ public class ZipPath extends Path { @Override public Iterator iterator() { - return new Iterator() { + return new Iterator<>() { private int i = 0; @Override @@ -803,7 +798,7 @@ public class ZipPath extends Path { @Override public SeekableByteChannel newByteChannel(OpenOption... options) throws IOException { - Set set = new HashSet(options.length); + Set set = new HashSet<>(options.length); Collections.addAll(set, options); return newByteChannel(set); } @@ -908,7 +903,7 @@ public class ZipPath extends Path { if (opt == REPLACE_EXISTING) replaceExisting = true; else if (opt == COPY_ATTRIBUTES) - copyAttrs = false; + copyAttrs = true; } // attributes of source file ZipFileAttributes zfas = getAttributes(); @@ -951,7 +946,9 @@ public class ZipPath extends Path { BasicFileAttributeView view = target.getFileAttributeView(BasicFileAttributeView.class); try { - view.setTimes(zfas.lastModifiedTime(), null, null); + view.setTimes(zfas.lastModifiedTime(), + zfas.lastAccessTime(), + zfas.creationTime()); } catch (IOException x) { // rollback? try { diff --git a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipUtils.java b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipUtils.java index b3106e09fa3dbebdeada37ee9111c9141df4425d..cddec6f9bef62d808a863e22ef914f03f678c9da 100644 --- a/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipUtils.java +++ b/src/share/demo/nio/zipfs/com/sun/nio/zipfs/ZipUtils.java @@ -36,6 +36,7 @@ import java.io.OutputStream; import java.util.Arrays; import java.util.Date; import java.util.regex.PatternSyntaxException; +import java.util.concurrent.TimeUnit; /** * @@ -48,7 +49,7 @@ 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 >>> 0) & 0xff); + os.write(v & 0xff); os.write((v >>> 8) & 0xff); } @@ -56,7 +57,7 @@ class ZipUtils { * 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 >>> 0) & 0xff)); + os.write((int)(v & 0xff)); os.write((int)((v >>> 8) & 0xff)); os.write((int)((v >>> 16) & 0xff)); os.write((int)((v >>> 24) & 0xff)); @@ -66,7 +67,7 @@ class ZipUtils { * 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 >>> 0) & 0xff)); + os.write((int)(v & 0xff)); os.write((int)((v >>> 8) & 0xff)); os.write((int)((v >>> 16) & 0xff)); os.write((int)((v >>> 24) & 0xff)); @@ -132,6 +133,27 @@ class ZipUtils { 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) { diff --git a/src/share/lib/security/sunpkcs11-solaris.cfg b/src/share/lib/security/sunpkcs11-solaris.cfg index 4ceccc3d08d14a9259bc2fedfce92196c2ed4867..d0b34ca134dcb1e643c856ef3b6682f654c83471 100644 --- a/src/share/lib/security/sunpkcs11-solaris.cfg +++ b/src/share/lib/security/sunpkcs11-solaris.cfg @@ -31,5 +31,9 @@ disabledMechanisms = { CKM_SHA256_RSA_PKCS CKM_SHA384_RSA_PKCS CKM_SHA512_RSA_PKCS +# the following mechanisms are disabled to ensure backward compatibility (Solaris bug 6545046) + CKM_DES_CBC_PAD + CKM_DES3_CBC_PAD + CKM_AES_CBC_PAD } diff --git a/test/demo/zipfs/ZipFSTester.java b/test/demo/zipfs/ZipFSTester.java index 54b9274db132fd0efbda2090781d6f41d9a77cf1..76a8d7aeed3f5e6e62f759c02fa26391cc9049b3 100644 --- a/test/demo/zipfs/ZipFSTester.java +++ b/test/demo/zipfs/ZipFSTester.java @@ -64,7 +64,6 @@ public class ZipFSTester { fs0.close(); // sync to file fs = newZipFileSystem(tmpfsPath, new HashMap()); - try { // prepare a src Path src = getTempPath(); @@ -146,13 +145,6 @@ public class ZipFSTester { Path fs2Path = getTempPath(); Path fs3Path = getTempPath(); - if (fs1Path.exists()) - fs1Path.delete(); - if (fs2Path.exists()) - fs2Path.delete(); - if (fs3Path.exists()) - fs3Path.delete(); - // create a new filesystem, copy everything from fs Map env = new HashMap(); env.put("createNew", true); @@ -280,7 +272,6 @@ public class ZipFSTester { walk(fs4.getPath("/")); System.out.println("closing: fs4"); fs4.close(); - System.out.printf("failed=%d%n", failed); fs1Path.delete(); @@ -426,6 +417,8 @@ public class ZipFSTester { } private static void mkdirs(Path path) throws IOException { + if (path.exists()) + return; path = path.toAbsolutePath(); Path parent = path.getParent(); if (parent != null) { diff --git a/test/java/nio/channels/AsynchronousSocketChannel/Leaky.java b/test/java/nio/channels/AsynchronousSocketChannel/Leaky.java index a950cc6676a2097e1e554695b3ad8a675c3f69a3..1ffd02e37d5a1e901dd12735388c04d142f208b2 100644 --- a/test/java/nio/channels/AsynchronousSocketChannel/Leaky.java +++ b/test/java/nio/channels/AsynchronousSocketChannel/Leaky.java @@ -22,15 +22,19 @@ */ /* @test - * @bug 4607272 + * @bug 4607272 6999915 * @summary Unit test for AsynchronousSocketChannel - * @run main/othervm -XX:+DisableExplicitGC -mx64m Leaky + * @run main/othervm -XX:+DisableExplicitGC -XX:MaxDirectMemorySize=64m Leaky */ import java.nio.ByteBuffer; +import java.nio.BufferPoolMXBean; import java.nio.channels.*; import java.net.*; +import java.util.List; import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.lang.management.ManagementFactory; /** * Heap buffers must be substituted with direct buffers when doing I/O. This @@ -49,13 +53,13 @@ public class Leaky { private final ByteBuffer dst; private Future readResult; - Connection() throws Exception { + Connection(AsynchronousChannelGroup group) throws Exception { ServerSocketChannel ssc = ServerSocketChannel.open().bind(new InetSocketAddress(0)); InetAddress lh = InetAddress.getLocalHost(); int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort(); SocketAddress remote = new InetSocketAddress(lh, port); - client = AsynchronousSocketChannel.open(); + client = AsynchronousSocketChannel.open(group); client.connect(remote).get(); peer = ssc.accept(); ssc.close(); @@ -77,11 +81,21 @@ public class Leaky { } public static void main(String[] args) throws Exception { + ThreadFactory threadFactory = new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + } + }; + AsynchronousChannelGroup group = + AsynchronousChannelGroup.withFixedThreadPool(4, threadFactory); final int CONNECTION_COUNT = 10; Connection[] connections = new Connection[CONNECTION_COUNT]; for (int i=0; i pools = + ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class); + for (BufferPoolMXBean pool: pools) + System.out.format(" %8s ", pool.getName()); + System.out.println(); + for (int i=0; i thisClass = new Object(){}.getClass().getEnclosingClass(); public static void main(String[] args) throws Throwable { new IteratorWeakConsistency().instanceMain(args);} public void instanceMain(String[] args) throws Throwable { diff --git a/test/java/util/jar/JarInputStream/BadSignedJar.jar b/test/java/util/jar/JarInputStream/BadSignedJar.jar new file mode 100644 index 0000000000000000000000000000000000000000..651c9ff3dd08818a97f506c1389a473fed1aab8b Binary files /dev/null and b/test/java/util/jar/JarInputStream/BadSignedJar.jar differ diff --git a/test/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java b/test/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java new file mode 100644 index 0000000000000000000000000000000000000000..977c10121dad0d19aba947a19a222e4136a1122a --- /dev/null +++ b/test/java/util/jar/JarInputStream/TestIndexedJarWithBadSignature.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6544278 + * @summary Confirm the JarInputStream throws the SecurityException when + * verifying an indexed jar file with corrupted signature + */ + +import java.io.IOException; +import java.io.FileInputStream; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class TestIndexedJarWithBadSignature { + + public static void main(String...args) throws Throwable { + try (JarInputStream jis = new JarInputStream( + new FileInputStream(System.getProperty("tst.src", ".") + + System.getProperty("file.separator") + + "BadSignedJar.jar"))) + { + JarEntry je1 = jis.getNextJarEntry(); + while(je1!=null){ + System.out.println("Jar Entry1==>"+je1.getName()); + je1 = jis.getNextJarEntry(); // This should throw Security Exception + } + throw new RuntimeException( + "Test Failed:Security Exception not being thrown"); + } catch (IOException ie){ + ie.printStackTrace(); + } catch (SecurityException e) { + System.out.println("Test passed: Security Exception thrown as expected"); + } + } +} diff --git a/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java b/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java new file mode 100644 index 0000000000000000000000000000000000000000..f0721daa2e5355600f25998b83dabeb849c4c4b0 --- /dev/null +++ b/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 6687725 + * @summary Test internal PKCS5Padding impl with various error conditions. + * @author Valerie Peng + * @library .. + */ +import java.io.*; +import java.nio.*; +import java.util.*; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; + +public class TestPKCS5PaddingError extends PKCS11Test { + private static class CI { // class for holding Cipher Information + String transformation; + String keyAlgo; + + CI(String transformation, String keyAlgo) { + this.transformation = transformation; + this.keyAlgo = keyAlgo; + } + } + + private static final CI[] TEST_LIST = { + // algorithms which use the native padding impl + new CI("DES/CBC/PKCS5Padding", "DES"), + new CI("DESede/CBC/PKCS5Padding", "DESede"), + new CI("AES/CBC/PKCS5Padding", "AES"), + // algorithms which use SunPKCS11's own padding impl + new CI("DES/ECB/PKCS5Padding", "DES"), + new CI("DESede/ECB/PKCS5Padding", "DESede"), + new CI("AES/ECB/PKCS5Padding", "AES"), + }; + + private static StringBuffer debugBuf = new StringBuffer(); + + public void main(Provider p) throws Exception { + boolean status = true; + Random random = new Random(); + + try { + byte[] plainText = new byte[200]; + + for (int i = 0; i < TEST_LIST.length; i++) { + CI currTest = TEST_LIST[i]; + System.out.println("===" + currTest.transformation + "==="); + try { + KeyGenerator kg = + KeyGenerator.getInstance(currTest.keyAlgo, p); + SecretKey key = kg.generateKey(); + Cipher c1 = Cipher.getInstance(currTest.transformation, + "SunJCE"); + c1.init(Cipher.ENCRYPT_MODE, key); + byte[] cipherText = c1.doFinal(plainText); + AlgorithmParameters params = c1.getParameters(); + Cipher c2 = Cipher.getInstance(currTest.transformation, p); + c2.init(Cipher.DECRYPT_MODE, key, params); + + // 1st test: wrong output length + // NOTE: Skip NSS since it reports CKR_DEVICE_ERROR when + // the data passed to its EncryptUpdate/DecryptUpdate is + // not multiple of blocks + if (!p.getName().equals("SunPKCS11-NSS")) { + try { + System.out.println("Testing with wrong cipherText length"); + c2.doFinal(cipherText, 0, cipherText.length - 2); + } catch (IllegalBlockSizeException ibe) { + // expected + } catch (Exception ex) { + System.out.println("Error: Unexpected Ex " + ex); + ex.printStackTrace(); + } + } + // 2nd test: wrong padding value + try { + System.out.println("Testing with wrong padding bytes"); + cipherText[cipherText.length - 1]++; + c2.doFinal(cipherText); + } catch (BadPaddingException bpe) { + // expected + } catch (Exception ex) { + System.out.println("Error: Unexpected Ex " + ex); + ex.printStackTrace(); + } + System.out.println("DONE"); + } catch (NoSuchAlgorithmException nsae) { + System.out.println("Skipping unsupported algorithm: " + + nsae); + } + } + } catch (Exception ex) { + // print out debug info when exception is encountered + if (debugBuf != null) { + System.out.println(debugBuf.toString()); + debugBuf = new StringBuffer(); + } + throw ex; + } + } + + public static void main(String[] args) throws Exception { + main(new TestPKCS5PaddingError()); + } +}