src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java
changeset 51638 2e4cf4ca074c
parent 50238 a9307f400f5a
child 51787 ba51515b64e5
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java	Tue Sep 04 14:35:59 2018 -0700
+++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java	Tue Sep 04 17:04:10 2018 -0700
@@ -317,7 +317,7 @@
                 if (inode == null)
                     return null;
                 e = new Entry(inode.name, inode.isdir);  // pseudo directory
-                e.method = METHOD_STORED;         // STORED for dir
+                e.method = METHOD_STORED;                // STORED for dir
                 e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp;
             }
         } finally {
@@ -1087,9 +1087,8 @@
             if (pos + CENHDR + nlen > limit) {
                 zerror("invalid CEN header (bad header size)");
             }
-            IndexNode inode = new IndexNode(cen, pos + CENHDR, nlen, pos);
+            IndexNode inode = new IndexNode(cen, nlen, pos);
             inodes.put(inode, inode);
-
             // skip ext and comment
             pos += (CENHDR + nlen + elen + clen);
         }
@@ -1173,9 +1172,15 @@
                 size = 16;
         }
         // read loc, use the original loc.elen/nlen
-        if (readFullyAt(buf, 0, LOCHDR , locoff) != LOCHDR)
+        //
+        // an extra byte after loc is read, which should be the first byte of the
+        // 'name' field of the loc. if this byte is '/', which means the original
+        // entry has an absolute path in original zip/jar file, the e.writeLOC()
+        // is used to output the loc, in which the leading "/" will be removed
+        if (readFullyAt(buf, 0, LOCHDR + 1 , locoff) != LOCHDR + 1)
             throw new ZipException("loc: reading failed");
-        if (updateHeader) {
+
+        if (updateHeader || LOCNAM(buf) > 0 && buf[LOCHDR] == '/') {
             locoff += LOCHDR + LOCNAM(buf) + LOCEXT(buf);  // skip header
             size += e.csize;
             written = e.writeLOC(os) + size;
@@ -1275,6 +1280,10 @@
                     if (inode.pos == -1) {
                         continue;               // pseudo directory node
                     }
+                    if (inode.name.length == 1 && inode.name[0] == '/') {
+                        continue;               // no root '/' directory even it
+                                                // exits in original zip/jar file.
+                    }
                     e = Entry.readCEN(this, inode);
                     try {
                         written += copyLOCEntry(e, false, os, written, buf);
@@ -1796,15 +1805,20 @@
             this.pos = pos;
         }
 
-        // constructor for cenInit()
-        IndexNode(byte[] cen, int noff, int nlen, int pos) {
+        // constructor for cenInit() (1) remove tailing '/' (2) pad leading '/'
+        IndexNode(byte[] cen, int nlen, int pos) {
+            int noff = pos + CENHDR;
             if (cen[noff + nlen - 1] == '/') {
                 isdir = true;
                 nlen--;
             }
-            name = new byte[nlen + 1];
-            System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
-            name[0] = '/';
+            if (nlen > 0 && cen[noff] == '/') {
+                name = Arrays.copyOfRange(cen, noff, noff + nlen);
+            } else {
+                name = new byte[nlen + 1];
+                System.arraycopy(cen, noff, name, 1, nlen);
+                name[0] = '/';
+            }
             name(name);
             this.pos = pos;
         }
@@ -2505,7 +2519,12 @@
     private void buildNodeTree() throws IOException {
         beginWrite();
         try {
-            IndexNode root = new IndexNode(ROOTPATH, true);
+            IndexNode root = inodes.get(LOOKUPKEY.as(ROOTPATH));
+            if (root == null) {
+                root = new IndexNode(ROOTPATH, true);
+            } else {
+                inodes.remove(root);
+            }
             IndexNode[] nodes = inodes.keySet().toArray(new IndexNode[0]);
             inodes.put(root, root);
             ParentLookup lookup = new ParentLookup();