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;
}