src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -312,11 +312,11 @@
             e = getEntry(path);
             if (e == null) {
                 IndexNode inode = getInode(path);
                 if (inode == null)
                     return null;
-                e = new Entry(inode.name);       // pseudo directory
+                e = new Entry(inode.name, inode.isdir);  // pseudo directory
                 e.method = METHOD_STORED;        // STORED for dir
                 e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp;
             }
         } finally {
             endRead();

@@ -398,11 +398,12 @@
             if (inode == null)
                 throw new NotDirectoryException(getString(path));
             List<Path> list = new ArrayList<>();
             IndexNode child = inode.child;
             while (child != null) {
-                ZipPath zp = new ZipPath(this, child.name);
+                // assume all path from zip file itself is "normalized"
+                ZipPath zp = new ZipPath(this, child.name, true);
                 if (filter == null || filter.accept(zp))
                     list.add(zp);
                 child = child.sibling;
             }
             return list.iterator();

@@ -413,18 +414,18 @@
 
     void createDirectory(byte[] dir, FileAttribute<?>... attrs)
         throws IOException
     {
         checkWritable();
-        dir = toDirectoryPath(dir);
+        //  dir = toDirectoryPath(dir);
         beginWrite();
         try {
             ensureOpen();
             if (dir.length == 0 || exists(dir))  // root dir, or exiting dir
                 throw new FileAlreadyExistsException(getString(dir));
             checkParents(dir);
-            Entry e = new Entry(dir, Entry.NEW);
+            Entry e = new Entry(dir, Entry.NEW, true);
             e.method = METHOD_STORED;            // STORED for dir
             update(e);
         } finally {
             endWrite();
         }

@@ -531,11 +532,11 @@
                 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));
+                return getOutputStream(new Entry(path, Entry.NEW, false));
             }
         } finally {
             endRead();
         }
     }

@@ -885,11 +886,11 @@
     private static byte[] ROOTPATH = new byte[] { '/' };
     private static byte[] getParent(byte[] path) {
         int off = getParentOff(path);
         if (off <= 1)
             return ROOTPATH;
-        return Arrays.copyOf(path, off + 1);
+        return Arrays.copyOf(path, off);
     }
 
     private static int getParentOff(byte[] path) {
         int off = path.length - 1;
         if (off > 0 && path[off] == '/')  // isDirectory

@@ -1073,15 +1074,13 @@
                 zerror("invalid CEN header (unsupported compression method: " + method + ")");
             }
             if (pos + CENHDR + nlen > limit) {
                 zerror("invalid CEN header (bad header size)");
             }
-            byte[] name = new byte[nlen + 1];
-            System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
-            name[0] = '/';
-            IndexNode inode = new IndexNode(name, pos);
+            IndexNode inode = new IndexNode(cen, pos + CENHDR, nlen, pos);
             inodes.put(inode, inode);
+
             // skip ext and comment
             pos += (CENHDR + nlen + elen + clen);
         }
         if (pos + ENDHDR != cen.length) {
             zerror("invalid CEN header (bad header size)");

@@ -1110,11 +1109,11 @@
     ////////////////////update & sync //////////////////////////////////////
 
     private boolean hasUpdate = false;
 
     // shared key. consumer guarantees the "writeLock" before use it.
-    private final IndexNode LOOKUPKEY = IndexNode.keyOf(null);
+    private final IndexNode LOOKUPKEY = new IndexNode(null, -1);
 
     private void updateDelete(IndexNode inode) {
         beginWrite();
         try {
             removeFromTree(inode);

@@ -1310,20 +1309,11 @@
     }
 
     IndexNode getInode(byte[] path) {
         if (path == null)
             throw new NullPointerException("path");
-        IndexNode key = IndexNode.keyOf(path);
-        IndexNode inode = inodes.get(key);
-        if (inode == null &&
-            (path.length == 0 || path[path.length -1] != '/')) {
-            // if does not ends with a slash
-            path = Arrays.copyOf(path, path.length + 1);
-            path[path.length - 1] = '/';
-            inode = inodes.get(key.as(path));
-        }
-        return inode;
+        return inodes.get(IndexNode.keyOf(path));
     }
 
     Entry getEntry(byte[] path) throws IOException {
         IndexNode inode = getInode(path);
         if (inode instanceof Entry)

@@ -1780,22 +1770,45 @@
     static class IndexNode {
         byte[] name;
         int    hashcode;  // node is hashable/hashed by its name
         int    pos = -1;  // position in cen table, -1 menas the
                           // entry does not exists in zip file
-        IndexNode(byte[] name) {
+        boolean isdir;
+
+        IndexNode(byte[] name, boolean isdir) {
             name(name);
+            this.isdir = isdir;
             this.pos = -1;
         }
 
         IndexNode(byte[] name, int pos) {
             name(name);
             this.pos = pos;
         }
 
+        // constructor for cenInit()
+        IndexNode(byte[] cen, int noff, int nlen, int pos) {
+            if (cen[noff + nlen - 1] == '/') {
+                isdir = true;
+                nlen--;
+            }
+            name = new byte[nlen + 1];
+            System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
+            name[0] = '/';
+            name(name);
+            this.pos = pos;
+        }
+
+        private static final ThreadLocal<IndexNode> cachedKey = new ThreadLocal<>();
+
         final static IndexNode keyOf(byte[] name) { // get a lookup key;
-            return new IndexNode(name, -1);
+            IndexNode key = cachedKey.get();
+            if (key == null) {
+                key = new IndexNode(name, -1);
+                cachedKey.set(key);
+            }
+            return key.as(name);
         }
 
         final void name(byte[] name) {
             this.name = name;
             this.hashcode = Arrays.hashCode(name);

@@ -1805,12 +1818,11 @@
             name(name);                             // as a lookup "key"
             return this;
         }
 
         boolean isDir() {
-            return name != null &&
-                   (name.length == 0 || name[name.length - 1] == '/');
+            return isdir;
         }
 
         public boolean equals(Object other) {
             if (!(other instanceof IndexNode)) {
                 return false;

@@ -1863,26 +1875,28 @@
         long   locoff;
         byte[] comment;
 
         Entry() {}
 
-        Entry(byte[] name) {
+        Entry(byte[] name, boolean isdir) {
             name(name);
+            this.isdir = isdir;
             this.mtime  = this.ctime = this.atime = System.currentTimeMillis();
             this.crc    = 0;
             this.size   = 0;
             this.csize  = 0;
             this.method = METHOD_DEFLATED;
         }
 
-        Entry(byte[] name, int type) {
-            this(name);
+        Entry(byte[] name, int type, boolean isdir) {
+            this(name, isdir);
             this.type = type;
         }
 
         Entry (Entry e, int type) {
             name(e.name);
+            this.isdir     = e.isdir;
             this.version   = e.version;
             this.ctime     = e.ctime;
             this.atime     = e.atime;
             this.mtime     = e.mtime;
             this.crc       = e.crc;

@@ -1900,11 +1914,11 @@
             this.comment   = e.comment;
             this.type      = type;
         }
 
         Entry (byte[] name, Path file, int type) {
-            this(name, type);
+            this(name, type, false);
             this.file = file;
             this.method = METHOD_STORED;
         }
 
         int version() throws ZipException {

@@ -1946,10 +1960,11 @@
             attrsEx     = CENATX(cen, pos);
             */
             locoff      = CENOFF(cen, pos);
             pos += CENHDR;
             this.name = inode.name;
+            this.isdir = inode.isdir;
             this.hashcode = inode.hashcode;
 
             pos += nlen;
             if (elen > 0) {
                 extra = Arrays.copyOfRange(cen, pos, pos + elen);

@@ -1972,13 +1987,14 @@
             int elen64   = 0;                // extra for ZIP64
             int elenNTFS = 0;                // extra for NTFS (a/c/mtime)
             int elenEXTT = 0;                // extra for Extended Timestamp
             boolean foundExtraTime = false;  // if time stamp NTFS, EXTT present
 
-            // confirm size/length
+            byte[] zname = isdir ? toDirectoryPath(name) : name;
 
-            int nlen = (name != null) ? name.length - 1 : 0;  // name has [0] as "slash"
+            // confirm size/length
+            int nlen = (zname != null) ? zname.length - 1 : 0;  // name has [0] as "slash"
             int elen = (extra != null) ? extra.length : 0;
             int eoff = 0;
             int clen = (comment != null) ? comment.length : 0;
             if (csize >= ZIP64_MINVAL) {
                 csize0 = ZIP64_MINVAL;

@@ -2035,11 +2051,11 @@
             }
             writeShort(os, 0);              // starting disk number
             writeShort(os, 0);              // internal file attributes (unused)
             writeInt(os, 0);                // external file attributes (unused)
             writeInt(os, locoff0);          // relative offset of local header
-            writeBytes(os, name, 1, nlen);
+            writeBytes(os, zname, 1, nlen);
             if (elen64 != 0) {
                 writeShort(os, EXTID_ZIP64);// Zip64 extra
                 writeShort(os, elen64 - 4); // size of "this" extra block
                 if (size0 == ZIP64_MINVAL)
                     writeLong(os, size);

@@ -2073,91 +2089,17 @@
                 writeBytes(os, comment);
             return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
         }
 
         ///////////////////// LOC //////////////////////
-        static Entry readLOC(ZipFileSystem zipfs, long pos)
-            throws IOException
-        {
-            return readLOC(zipfs, pos, new byte[1024]);
-        }
-
-        static Entry readLOC(ZipFileSystem zipfs, long pos, byte[] buf)
-            throws IOException
-        {
-            return new Entry().loc(zipfs, pos, buf);
-        }
-
-        Entry loc(ZipFileSystem zipfs, long pos, byte[] buf)
-            throws IOException
-        {
-            assert (buf.length >= LOCHDR);
-            if (zipfs.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR)
-                throw new ZipException("loc: reading failed");
-            if (!locSigAt(buf, 0))
-                throw new ZipException("loc: wrong sig ->"
-                                       + Long.toString(getSig(buf, 0), 16));
-            //startPos = pos;
-            version  = LOCVER(buf);
-            flag     = LOCFLG(buf);
-            method   = LOCHOW(buf);
-            mtime    = dosToJavaTime(LOCTIM(buf));
-            crc      = LOCCRC(buf);
-            csize    = LOCSIZ(buf);
-            size     = LOCLEN(buf);
-            int nlen = LOCNAM(buf);
-            int elen = LOCEXT(buf);
-
-            name = new byte[nlen + 1];
-            name[0] = '/';
-            if (zipfs.readFullyAt(name, 1, nlen, pos + LOCHDR) != nlen) {
-                throw new ZipException("loc: name reading failed");
-            }
-            if (elen > 0) {
-                extra = new byte[elen];
-                if (zipfs.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen)
-                    != elen) {
-                    throw new ZipException("loc: ext reading failed");
-                }
-            }
-            pos += (LOCHDR + nlen + elen);
-            if ((flag & FLAG_DATADESCR) != 0) {
-                // Data Descriptor
-                Entry e = zipfs.getEntry(name);  // get the size/csize from cen
-                if (e == null)
-                    throw new ZipException("loc: name not found in cen");
-                size = e.size;
-                csize = e.csize;
-                pos += (method == METHOD_STORED ? size : csize);
-                if (size >= ZIP64_MINVAL || csize >= ZIP64_MINVAL)
-                    pos += 24;
-                else
-                    pos += 16;
-            } else {
-                if (extra != null &&
-                    (size == ZIP64_MINVAL || csize == ZIP64_MINVAL)) {
-                    // zip64 ext: must include both size and csize
-                    int off = 0;
-                    while (off + 20 < elen) {    // HeaderID+DataSize+Data
-                        int sz = SH(extra, off + 2);
-                        if (SH(extra, off) == EXTID_ZIP64 && sz == 16) {
-                            size = LL(extra, off + 4);
-                            csize = LL(extra, off + 12);
-                            break;
-                        }
-                        off += (sz + 4);
-                    }
-                }
-                pos += (method == METHOD_STORED ? size : csize);
-            }
-            return this;
-        }
 
         int writeLOC(OutputStream os) throws IOException {
             writeInt(os, LOCSIG);               // LOC header signature
             int version = version();
-            int nlen = (name != null) ? name.length - 1 : 0; // [0] is slash
+
+            byte[] zname = isdir ? toDirectoryPath(name) : name;
+            int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
             int elen = (extra != null) ? extra.length : 0;
             boolean foundExtraTime = false;     // if extra timestamp present
             int eoff = 0;
             int elen64 = 0;
             int elenEXTT = 0;

@@ -2212,11 +2154,11 @@
                         elenEXTT += 4;
                 }
             }
             writeShort(os, nlen);
             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
-            writeBytes(os, name, 1, nlen);
+            writeBytes(os, zname, 1, nlen);
             if (elen64 != 0) {
                 writeShort(os, EXTID_ZIP64);
                 writeShort(os, 16);
                 writeLong(os, size);
                 writeLong(os, csize);

@@ -2549,11 +2491,11 @@
     }
 
     private void buildNodeTree() throws IOException {
         beginWrite();
         try {
-            IndexNode root = new IndexNode(ROOTPATH);
+            IndexNode root = new IndexNode(ROOTPATH, true);
             IndexNode[] nodes = inodes.keySet().toArray(new IndexNode[0]);
             inodes.put(root, root);
             ParentLookup lookup = new ParentLookup();
             for (IndexNode node : nodes) {
                 IndexNode parent;

@@ -2562,19 +2504,19 @@
                     if (off <= 1) {    // parent is root
                         node.sibling = root.child;
                         root.child = node;
                         break;
                     }
-                    lookup = lookup.as(node.name, off + 1);
+                    lookup = lookup.as(node.name, off);
                     if (inodes.containsKey(lookup)) {
                         parent = inodes.get(lookup);
                         node.sibling = parent.child;
                         parent.child = node;
                         break;
                     }
                     // add new pseudo directory entry
-                    parent = new IndexNode(Arrays.copyOf(node.name, off + 1));
+                    parent = new IndexNode(Arrays.copyOf(node.name, off), true);
                     inodes.put(parent, parent);
                     node.sibling = parent.child;
                     parent.child = node;
                     node = parent;
                 }