提交 f6b07ee4 编写于 作者: S sherman

6990846: Demo: NIO.2 filesystem provider for zip/jar archives

Summary: The first drop of the zip filesystem provider, as a separate demo
Reviewed-by: alanb
上级 67166499
...@@ -31,7 +31,7 @@ BUILDDIR = .. ...@@ -31,7 +31,7 @@ BUILDDIR = ..
PRODUCT = demos PRODUCT = demos
include $(BUILDDIR)/common/Defs.gmk include $(BUILDDIR)/common/Defs.gmk
SUBDIRS = jni SUBDIRS = jni nio
SUBDIRS_desktop = applets jfc SUBDIRS_desktop = applets jfc
SUBDIRS_management = management SUBDIRS_management = management
SUBDIRS_misc = scripting SUBDIRS_misc = scripting
......
#
# Copyright (c) 1997, 2007, 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
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# 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.
#
#
# Makefile for building the jfc demos
#
BUILDDIR = ../..
PRODUCT = demos
include $(BUILDDIR)/common/Defs.gmk
SUBDIRS = zipfs
include $(BUILDDIR)/common/Subdirs.gmk
all build clean clobber::
$(SUBDIRS-loop)
#
# Copyright (c) 1997, 2002, 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
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# 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.
#
#
# Makefile to build the ZipFileSystem demo.
#
BUILDDIR = ../../..
PRODUCT = demo/zipfs
DEMONAME = zipfs
include $(BUILDDIR)/common/Defs.gmk
DEMO_ROOT = $(SHARE_SRC)/demo/nio/$(DEMONAME)
DEMO_TOPFILES = ./README.txt
DEMO_SRCDIR = $(DEMO_ROOT)
DEMO_DESTDIR = $(DEMODIR)/nio/$(DEMONAME)
#
# Demo jar building rules.
#
include $(BUILDDIR)/common/Demo.gmk
/*
* Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.net.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.file.StandardCopyOption.*;
/*
* ZipFileSystem usage demo
*
* java [-cp .../zipfs.jar:./] Demo action ZipfileName [...]
*
* To deploy the provider, either copy the zipfs.jar into JDK/JRE
* extensions directory or add
* <JDK_HOME>/demo/nio/ZipFileSystem/zipfs.jar
* into your class path as showed above.
*
* @author Xueming Shen
*/
public class Demo {
static enum Action {
rename, // <java Demo rename zipfile src dst>
// rename entry src to dst inside zipfile
movein, // <java Demo movein zipfile src dst>
// move an external src file into zipfile
// as entry dst
moveout, // <java Demo moveout zipfile src dst>
// move a zipfile entry src out to dst
copy, // <java Demo copy zipfile src dst>
// copy entry src to dst inside zipfile
copyin, // <java Demo copyin zipfile src dst>
// copy an external src file into zipfile
// as entry dst
copyout, // <java Demo copyout zipfile src dst>
// copy zipfile entry src" out to file dst
zzmove, // <java Demo zzmove zfsrc zfdst path>
// move entry path/dir from zfsrc to zfdst
zzcopy, // <java Demo zzcopy zfsrc zfdst path>
// copy path from zipfile zfsrc to zipfile
// zfdst
attrs, // <java Demo attrs zipfile path>
// printout the attributes of entry path
attrsspace, // <java Demo attrsspace zipfile path>
// printout the storespace attrs of entry path
setmtime, // <java Demo setmtime zipfile "MM/dd/yy-HH:mm:ss" path...>
// set the lastModifiedTime of entry path
lsdir, // <java Demo lsdir zipfile dir>
// list dir's direct child files/dirs
mkdir, // <java Demo mkdir zipfile dir>
mkdirs, // <java Demo mkdirs zipfile dir>
rmdirs, // <java Demo rmdirs zipfile dir>
list, // <java Demo list zipfile [dir]>
// recursively list all entries of dir
// via DirectoryStream
tlist, // <java Demo tlist zipfile [dir]>
// list with buildDirTree=true
vlist, // <java Demo vlist zipfile [dir]>
// recursively verbose list all entries of
// dir via DirectoryStream
walk, // <java Demo walk zipfile [dir]>
// recursively walk all entries of dir
// via Files.walkFileTree
twalk, // <java Demo twalk zipfile [dir]>
// walk with buildDirTree=true
extract, // <java Demo extract zipfile file [...]>
update, // <java Demo extract zipfile file [...]>
delete, // <java Demo delete zipfile file [...]>
add, // <java Demo add zipfile file [...]>
create, // <java Demo create zipfile file [...]>
// create a new zipfile if it doesn't exit
// and then add the file(s) into it.
attrs2, // <java Demo attrs2 zipfile file [...]>
// test different ways to print attrs
}
public static void main(String[] args) throws Throwable {
Action action = Action.valueOf(args[0]);;
Map<String, Object> env = env = new HashMap<String, Object>();
if (action == Action.create)
env.put("createNew", true);
if (action == Action.tlist || action == Action.twalk)
env.put("buildDirTree", true);
FileSystem fs = FileSystems.newFileSystem(
URI.create("zip" + Paths.get(args[1]).toUri().toString().substring(4)),
env,
null);
try {
FileSystem fs2;
Path path, src, dst;
boolean isRename = false;
switch (action) {
case rename:
src = fs.getPath(args[2]);
dst = fs.getPath(args[3]);
src.moveTo(dst);
break;
case moveout:
src = fs.getPath(args[2]);
dst = Paths.get(args[3]);
src.moveTo(dst);
break;
case movein:
src = Paths.get(args[2]);
dst = fs.getPath(args[3]);
src.moveTo(dst);
break;
case copy:
src = fs.getPath(args[2]);
dst = fs.getPath(args[3]);
src.copyTo(dst);
break;
case copyout:
src = fs.getPath(args[2]);
dst = Paths.get(args[3]);
src.copyTo(dst);
break;
case copyin:
src = Paths.get(args[2]);
dst = fs.getPath(args[3]);
src.copyTo(dst);
break;
case zzmove:
fs2 = FileSystems.newFileSystem(
URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)),
env,
null);
//sf1.getPath(args[3]).moveTo(fs2.getPath(args[3]));
z2zmove(fs, fs2, args[3]);
fs2.close();
break;
case zzcopy:
fs2 = FileSystems.newFileSystem(
URI.create("zip" + Paths.get(args[2]).toUri().toString().substring(4)),
env,
null);
//sf1.getPath(args[3]).copyTo(fs2.getPath(args[3]));
z2zcopy(fs, fs2, args[3]);
fs2.close();
break;
case attrs:
for (int i = 2; i < args.length; i++) {
path = fs.getPath(args[i]);
System.out.println(
Attributes.readBasicFileAttributes(path).toString());
}
break;
case setmtime:
DateFormat df = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
Date newDatetime = df.parse(args[2]);
for (int i = 3; i < args.length; i++) {
path = fs.getPath(args[i]);
path.setAttribute("lastModifiedTime",
FileTime.fromMillis(newDatetime.getTime()));
System.out.println(
Attributes.readBasicFileAttributes(path).toString());
}
break;
case attrsspace:
path = fs.getPath("/");
FileStore fstore = path.getFileStore();
//System.out.println(fstore.getFileStoreAttributeView(FileStoreSpaceAttributeView.class)
// .readAttributes());
// or
System.out.printf("filestore[%s]%n", fstore.name());
System.out.printf(" totalSpace: %d%n",
(Long)fstore.getAttribute("space:totalSpace"));
System.out.printf(" usableSpace: %d%n",
(Long)fstore.getAttribute("space:usableSpace"));
System.out.printf(" unallocSpace: %d%n",
(Long)fstore.getAttribute("space:unallocatedSpace"));
break;
case list:
case tlist:
if (args.length < 3)
list(fs.getPath("/"), false);
else
list(fs.getPath(args[2]), false);
break;
case vlist:
if (args.length < 3)
list(fs.getPath("/"), true);
else
list(fs.getPath(args[2]), true);
break;
case twalk:
case walk:
walk(fs.getPath((args.length > 2)? args[2] : "/"));
break;
case extract:
if (args.length == 2) {
extract(fs, "/");
} else {
for (int i = 2; i < args.length; i++) {
extract(fs, args[i]);
}
}
break;
case delete:
for (int i = 2; i < args.length; i++)
fs.getPath(args[i]).delete();
break;
case create:
case add:
case update:
for (int i = 2; i < args.length; i++) {
update(fs, args[i]);
}
break;
case lsdir:
path = fs.getPath(args[2]);
final String fStr = (args.length > 3)?args[3]:"";
DirectoryStream<Path> ds = path.newDirectoryStream(
new DirectoryStream.Filter<Path>() {
public boolean accept(Path path) {
return path.toString().contains(fStr);
}
});
for (Path p : ds)
System.out.println(p);
break;
case mkdir:
fs.getPath(args[2]).createDirectory();
break;
case mkdirs:
mkdirs(fs.getPath(args[2]));
break;
case attrs2:
for (int i = 2; i < args.length; i++) {
path = fs.getPath(args[i]);
System.out.println("-------(1)---------");
System.out.println(
Attributes.readBasicFileAttributes(path).toString());
System.out.println("-------(2)---------");
Map<String, ?> map = path.readAttributes("zip:*");
for (Map.Entry<String, ?> e : map.entrySet()) {
System.out.printf(" %s : %s%n", e.getKey(), e.getValue());
}
System.out.println("-------(3)---------");
map = path.readAttributes("size,lastModifiedTime,isDirectory");
for (Map.Entry<String, ?> e : map.entrySet()) {
System.out.printf(" %s : %s%n", e.getKey(), e.getValue());
}
}
break;
}
} catch (Exception x) {
x.printStackTrace();
} finally {
if (fs != null)
fs.close();
}
}
private static byte[] getBytes(String name) {
return name.getBytes();
}
private static String getString(byte[] name) {
return new String(name);
}
private static void walk(Path path) throws IOException
{
Files.walkFileTree(
path,
new SimpleFileVisitor<Path>() {
private int indent = 0;
private void indent() {
int n = 0;
while (n++ < indent)
System.out.printf(" ");
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs)
{
indent();
System.out.printf("%s%n", file.getName().toString());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs)
{
indent();
System.out.printf("[%s]%n", dir.toString());
indent += 2;
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir,
IOException ioe)
{
indent -= 2;
return FileVisitResult.CONTINUE;
}
});
}
private static void update(FileSystem fs, String path) throws Throwable{
Path src = FileSystems.getDefault().getPath(path);
if (Boolean.TRUE.equals(src.getAttribute("isDirectory"))) {
DirectoryStream<Path> ds = src.newDirectoryStream();
for (Path child : ds)
update(fs, child.toString());
ds.close();
} else {
Path dst = fs.getPath(path);
Path parent = dst.getParent();
if (parent != null && parent.notExists())
mkdirs(parent);
src.copyTo(dst, REPLACE_EXISTING);
}
}
private static void extract(FileSystem fs, String path) throws Throwable{
Path src = fs.getPath(path);
if (Boolean.TRUE.equals(src.getAttribute("isDirectory"))) {
DirectoryStream<Path> ds = src.newDirectoryStream();
for (Path child : ds)
extract(fs, child.toString());
ds.close();
} else {
if (path.startsWith("/"))
path = path.substring(1);
Path dst = FileSystems.getDefault().getPath(path);
Path parent = dst.getParent();
if (parent.notExists())
mkdirs(parent);
src.copyTo(dst, REPLACE_EXISTING);
}
}
// use DirectoryStream
private static void z2zcopy(FileSystem src, FileSystem dst, String path)
throws IOException
{
Path srcPath = src.getPath(path);
Path dstPath = dst.getPath(path);
if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) {
if (!dstPath.exists()) {
try {
mkdirs(dstPath);
} catch (FileAlreadyExistsException x) {}
}
DirectoryStream<Path> ds = srcPath.newDirectoryStream();
for (Path child : ds) {
z2zcopy(src, dst,
path + (path.endsWith("/")?"":"/") + child.getName());
}
ds.close();
} else {
//System.out.println("copying..." + path);
srcPath.copyTo(dstPath);
}
}
// use TreeWalk to move
private static void z2zmove(FileSystem src, FileSystem dst, String path)
throws IOException
{
final Path srcPath = src.getPath(path).toAbsolutePath();
final Path dstPath = dst.getPath(path).toAbsolutePath();
Files.walkFileTree(srcPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs)
{
Path dst = srcPath.relativize(file);
dst = dstPath.resolve(dst);
try {
Path parent = dstPath.getParent();
if (parent != null && parent.notExists())
mkdirs(parent);
file.moveTo(dst);
} catch (IOException x) {
x.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs)
{
Path dst = srcPath.relativize(dir);
dst = dstPath.resolve(dst);
try {
if (dst.notExists())
mkdirs(dst);
} catch (IOException x) {
x.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir,
IOException ioe)
throws IOException
{
try {
dir.delete();
} catch (IOException x) {
//x.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
});
}
private static void mkdirs(Path path) throws IOException {
path = path.toAbsolutePath();
Path parent = path.getParent();
if (parent != null) {
if (parent.notExists())
mkdirs(parent);
}
path.createDirectory();
}
private static void rmdirs(Path path) throws IOException {
while (path != null && path.getNameCount() != 0) {
path.delete();
path = path.getParent();
}
}
private static void list(Path path, boolean verbose ) throws IOException {
if (verbose)
System.out.println(Attributes.readBasicFileAttributes(path).toString());
else
System.out.printf(" %s%n", path.toString());
if (path.notExists())
return;
if (Attributes.readBasicFileAttributes(path).isDirectory()) {
DirectoryStream<Path> ds = path.newDirectoryStream();
for (Path child : ds)
list(child, verbose);
ds.close();
}
}
// check the content of two paths are equal
private static void checkEqual(Path src, Path dst) throws IOException
{
//System.out.printf("checking <%s> vs <%s>...%n",
// src.toString(), dst.toString());
//streams
InputStream isSrc = src.newInputStream();
InputStream isDst = dst.newInputStream();
byte[] bufSrc = new byte[8192];
byte[] bufDst = new byte[8192];
try {
int nSrc = 0;
while ((nSrc = isSrc.read(bufSrc)) != -1) {
int nDst = 0;
while (nDst < nSrc) {
int n = isDst.read(bufDst, nDst, nSrc - nDst);
if (n == -1) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
nDst += n;
}
while (--nSrc >= 0) {
if (bufSrc[nSrc] != bufDst[nSrc]) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
nSrc--;
}
}
} finally {
isSrc.close();
isDst.close();
}
// channels
SeekableByteChannel chSrc = src.newByteChannel();
SeekableByteChannel chDst = dst.newByteChannel();
if (chSrc.size() != chDst.size()) {
System.out.printf("src[%s].size=%d, dst[%s].size=%d%n",
chSrc.toString(), chSrc.size(),
chDst.toString(), chDst.size());
throw new RuntimeException("CHECK FAILED!");
}
ByteBuffer bbSrc = ByteBuffer.allocate(8192);
ByteBuffer bbDst = ByteBuffer.allocate(8192);
try {
int nSrc = 0;
while ((nSrc = chSrc.read(bbSrc)) != -1) {
int nDst = chDst.read(bbDst);
if (nSrc != nDst) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
while (--nSrc >= 0) {
if (bbSrc.get(nSrc) != bbDst.get(nSrc)) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
nSrc--;
}
bbSrc.flip();
bbDst.flip();
}
} catch (IOException x) {
x.printStackTrace();
} finally {
chSrc.close();
chDst.close();
}
}
private static void fchCopy(Path src, Path dst) throws IOException
{
Set<OpenOption> read = new HashSet<>();
read.add(READ);
Set<OpenOption> openwrite = new HashSet<>();
openwrite.add(CREATE_NEW);
openwrite.add(WRITE);
FileChannel srcFc = src.getFileSystem()
.provider()
.newFileChannel(src, read);
FileChannel dstFc = dst.getFileSystem()
.provider()
.newFileChannel(dst, openwrite);
try {
ByteBuffer bb = ByteBuffer.allocate(8192);
while (srcFc.read(bb) >= 0) {
bb.flip();
dstFc.write(bb);
bb.clear();
}
} finally {
srcFc.close();
dstFc.close();
}
}
private static void chCopy(Path src, Path dst) throws IOException
{
Set<OpenOption> read = new HashSet<>();
read.add(READ);
Set<OpenOption> openwrite = new HashSet<>();
openwrite.add(CREATE_NEW);
openwrite.add(WRITE);
SeekableByteChannel srcCh = src.newByteChannel(read);
SeekableByteChannel dstCh = dst.newByteChannel(openwrite);
try {
ByteBuffer bb = ByteBuffer.allocate(8192);
while (srcCh.read(bb) >= 0) {
bb.flip();
dstCh.write(bb);
bb.clear();
}
} finally {
srcCh.close();
dstCh.close();
}
}
private static void streamCopy(Path src, Path dst) throws IOException
{
InputStream isSrc = src.newInputStream();
OutputStream osDst = dst.newOutputStream();
byte[] buf = new byte[8192];
try {
int n = 0;
while ((n = isSrc.read(buf)) != -1) {
osDst.write(buf, 0, n);
}
} finally {
isSrc.close();
osDst.close();
}
}
}
com.sun.nio.zipfs.ZipFileSystemProvider
com.sun.nio.zipfs.JarFileSystemProvider
ZipFileSystem is a file system provider that treats the contents of a zip or
JAR file as a java.nio.file.FileSystem.
To deploy the provider you must copy zipfs.jar into your extensions
directory or else add <JDK_HOME>/demo/nio/ZipFileSystem/zipfs.jar
to your class path.
The factory methods defined by the java.nio.file.FileSystems class can be
used to create a FileSystem, eg:
// use file type detection
Map<String,?> env = Collections.emptyMap();
Path jarfile = Path.get("foo.jar");
FileSystem fs = FileSystems.newFileSystem(jarfile, env);
-or
// locate file system by URI
Map<String,?> env = Collections.emptyMap();
URI uri = URI.create("zip:///mydir/foo.jar");
FileSystem fs = FileSystems.newFileSystem(uri, env);
Once a FileSystem is created then classes in the java.nio.file package
can be used to access files in the zip/JAR file, eg:
Path mf = fs.getPath("/META-INF/MANIFEST.MF");
InputStream in = mf.newInputStream();
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Sun Microsystems nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.nio.file.*;
import java.nio.file.spi.*;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileSystemProvider;
import java.net.URI;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class JarFileSystemProvider extends ZipFileSystemProvider
{
@Override
public String getScheme() {
return "jar";
}
@Override
protected Path uriToPath(URI uri) {
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
}
try {
String uristr = uri.toString();
int end = uristr.indexOf("!/");
uristr = uristr.substring(4, (end == -1) ? uristr.length() : end);
uri = new URI(uristr);
return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null))
.toAbsolutePath();
} catch (URISyntaxException e) {
throw new AssertionError(e); //never thrown
}
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
/**
* Utility class for zipfile name and comment decoding and encoding
*
* @author Xueming Shen
*/
final class ZipCoder {
String toString(byte[] ba, int length) {
CharsetDecoder cd = decoder().reset();
int len = (int)(length * cd.maxCharsPerByte());
char[] ca = new char[len];
if (len == 0)
return new String(ca);
ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
}
String toString(byte[] ba) {
return toString(ba, ba.length);
}
byte[] getBytes(String s) {
CharsetEncoder ce = encoder().reset();
char[] ca = s.toCharArray();
int len = (int)(ca.length * ce.maxBytesPerChar());
byte[] ba = new byte[len];
if (len == 0)
return ba;
ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = ce.flush(bb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
if (bb.position() == ba.length) // defensive copy?
return ba;
else
return Arrays.copyOf(ba, bb.position());
}
// assume invoked only if "this" is not utf8
byte[] getBytesUTF8(String s) {
if (isutf8)
return getBytes(s);
if (utf8 == null)
utf8 = new ZipCoder(Charset.forName("UTF-8"));
return utf8.getBytes(s);
}
String toStringUTF8(byte[] ba, int len) {
if (isutf8)
return toString(ba, len);
if (utf8 == null)
utf8 = new ZipCoder(Charset.forName("UTF-8"));
return utf8.toString(ba, len);
}
boolean isUTF8() {
return isutf8;
}
private Charset cs;
private boolean isutf8;
private ZipCoder utf8;
private ZipCoder(Charset cs) {
this.cs = cs;
this.isutf8 = cs.name().equals("UTF-8");
}
static ZipCoder get(Charset charset) {
return new ZipCoder(charset);
}
static ZipCoder get(String csn) {
try {
return new ZipCoder(Charset.forName(csn));
} catch (Throwable t) {
t.printStackTrace();
}
return new ZipCoder(Charset.defaultCharset());
}
private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>();
private final ThreadLocal<CharsetEncoder> encTL = new ThreadLocal<>();
private CharsetDecoder decoder() {
CharsetDecoder dec = decTL.get();
if (dec == null) {
dec = cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
decTL.set(dec);
}
return dec;
}
private CharsetEncoder encoder() {
CharsetEncoder enc = encTL.get();
if (enc == null) {
enc = cs.newEncoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
encTL.set(enc);
}
return enc;
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.nio.ByteBuffer;
/**
*
* @author Xueming Shen
*/
class ZipConstants {
/*
* Compression methods
*/
static final int METHOD_STORED = 0;
static final int METHOD_DEFLATED = 8;
static final int METHOD_DEFLATED64 = 9;
static final int METHOD_BZIP2 = 12;
static final int METHOD_LZMA = 14;
static final int METHOD_LZ77 = 19;
/*
* General purpose big flag
*/
static final int FLAG_ENCRYPTED = 0x01;
static final int FLAG_DATADESCR = 0x08; // crc, size and csize in dd
static final int FLAG_EFS = 0x800; // If this bit is set the filename and
// comment fields for this file must be
// encoded using UTF-8.
/*
* Header signatures
*/
static long LOCSIG = 0x04034b50L; // "PK\003\004"
static long EXTSIG = 0x08074b50L; // "PK\007\008"
static long CENSIG = 0x02014b50L; // "PK\001\002"
static long ENDSIG = 0x06054b50L; // "PK\005\006"
/*
* Header sizes in bytes (including signatures)
*/
static final int LOCHDR = 30; // LOC header size
static final int EXTHDR = 16; // EXT header size
static final int CENHDR = 46; // CEN header size
static final int ENDHDR = 22; // END header size
/*
* Local file (LOC) header field offsets
*/
static final int LOCVER = 4; // version needed to extract
static final int LOCFLG = 6; // general purpose bit flag
static final int LOCHOW = 8; // compression method
static final int LOCTIM = 10; // modification time
static final int LOCCRC = 14; // uncompressed file crc-32 value
static final int LOCSIZ = 18; // compressed size
static final int LOCLEN = 22; // uncompressed size
static final int LOCNAM = 26; // filename length
static final int LOCEXT = 28; // extra field length
/*
* Extra local (EXT) header field offsets
*/
static final int EXTCRC = 4; // uncompressed file crc-32 value
static final int EXTSIZ = 8; // compressed size
static final int EXTLEN = 12; // uncompressed size
/*
* Central directory (CEN) header field offsets
*/
static final int CENVEM = 4; // version made by
static final int CENVER = 6; // version needed to extract
static final int CENFLG = 8; // encrypt, decrypt flags
static final int CENHOW = 10; // compression method
static final int CENTIM = 12; // modification time
static final int CENCRC = 16; // uncompressed file crc-32 value
static final int CENSIZ = 20; // compressed size
static final int CENLEN = 24; // uncompressed size
static final int CENNAM = 28; // filename length
static final int CENEXT = 30; // extra field length
static final int CENCOM = 32; // comment length
static final int CENDSK = 34; // disk number start
static final int CENATT = 36; // internal file attributes
static final int CENATX = 38; // external file attributes
static final int CENOFF = 42; // LOC header offset
/*
* End of central directory (END) header field offsets
*/
static final int ENDSUB = 8; // number of entries on this disk
static final int ENDTOT = 10; // total number of entries
static final int ENDSIZ = 12; // central directory size in bytes
static final int ENDOFF = 16; // offset of first CEN header
static final int ENDCOM = 20; // zip file comment length
/*
* ZIP64 constants
*/
static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006"
static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007"
static final int ZIP64_ENDHDR = 56; // ZIP64 end header size
static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size
static final int ZIP64_EXTHDR = 24; // EXT header size
static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID
static final int ZIP64_MINVAL32 = 0xFFFF;
static final long ZIP64_MINVAL = 0xFFFFFFFFL;
/*
* Zip64 End of central directory (END) header field offsets
*/
static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir
static final int ZIP64_ENDVEM = 12; // version made by
static final int ZIP64_ENDVER = 14; // version needed to extract
static final int ZIP64_ENDNMD = 16; // number of this disk
static final int ZIP64_ENDDSK = 20; // disk number of start
static final int ZIP64_ENDTOD = 24; // total number of entries on this disk
static final int ZIP64_ENDTOT = 32; // total number of entries
static final int ZIP64_ENDSIZ = 40; // central directory size in bytes
static final int ZIP64_ENDOFF = 48; // offset of first CEN header
static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector
/*
* Zip64 End of central directory locator field offsets
*/
static final int ZIP64_LOCDSK = 4; // disk number start
static final int ZIP64_LOCOFF = 8; // offset of zip64 end
static final int ZIP64_LOCTOT = 16; // total number of disks
/*
* Zip64 Extra local (EXT) header field offsets
*/
static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value
static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
/*
* Extra field header ID
*/
static final int EXTID_ZIP64 = 0x0001; // ZIP64
static final int EXTID_NTFS = 0x000a; // NTFS
static final int EXTID_UNIX = 0x000d; // UNIX
/*
* fields access methods
*/
///////////////////////////////////////////////////////
static final int CH(byte[] b, int n) {
return b[n] & 0xff;
}
static final int SH(byte[] b, int n) {
return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8);
}
static final long LG(byte[] b, int n) {
return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL;
}
static final long LL(byte[] b, int n) {
return (LG(b, n)) | (LG(b, n + 4) << 32);
}
static final long GETSIG(byte[] b) {
return LG(b, 0);
}
// local file (LOC) header fields
static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature
static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract
static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags
static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method
static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time
static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data
static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size
static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size
static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length
static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length
// extra local (EXT) header fields
static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data
static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size
static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size
// end of central directory header (END) fields
static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk
static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries
static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size
static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset
static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment
static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);}
// zip64 end of central directory recoder fields
static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk
static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries
static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size
static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset
static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset
//////////////////////////////////////////
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);}
/* The END header is followed by a variable length comment of size < 64k. */
static final long END_MAXLEN = 0xFFFF + ENDHDR;
static final int READBLOCKSZ = 128;
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.nio.file.DirectoryStream;
import java.nio.file.ClosedDirectoryStreamException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.IOException;
import static com.sun.nio.zipfs.ZipUtils.*;
/**
*
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipDirectoryStream implements DirectoryStream<Path> {
private final ZipFileSystem zipfs;
private final byte[] path;
private final DirectoryStream.Filter<? super Path> filter;
private volatile boolean isClosed;
private volatile Iterator<Path> itr;
ZipDirectoryStream(ZipPath zipPath,
DirectoryStream.Filter<? super java.nio.file.Path> filter)
throws IOException
{
this.zipfs = zipPath.getFileSystem();
this.path = zipPath.getResolvedPath();
this.filter = filter;
// sanity check
if (!zipfs.isDirectory(path))
throw new NotDirectoryException(zipPath.toString());
}
@Override
public synchronized Iterator<Path> iterator() {
if (isClosed)
throw new ClosedDirectoryStreamException();
if (itr != null)
throw new IllegalStateException("Iterator has already been returned");
try {
itr = zipfs.iteratorOf(path, filter);
} catch (IOException e) {
throw new IllegalStateException(e);
}
return new Iterator<Path>() {
private Path next;
@Override
public boolean hasNext() {
if (isClosed)
return false;
return itr.hasNext();
}
@Override
public synchronized Path next() {
if (isClosed)
throw new NoSuchElementException();
return itr.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public synchronized void close() throws IOException {
isClosed = true;
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.nio.file.ReadOnlyFileSystemException;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileTime;
import java.io.IOException;
import java.util.LinkedHashMap;
/*
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipFileAttributeView implements BasicFileAttributeView
{
private static enum AttrID {
size,
creationTime,
lastAccessTime,
lastModifiedTime,
isDirectory,
isRegularFile,
isSymbolicLink,
isOther,
fileKey,
compressedSize,
crc,
method
};
private final ZipPath path;
private final boolean isZipView;
private ZipFileAttributeView(ZipPath path, boolean isZipView) {
this.path = path;
this.isZipView = isZipView;
}
static <V extends FileAttributeView> V get(ZipPath path, Class<V> type) {
if (type == null)
throw new NullPointerException();
if (type == BasicFileAttributeView.class)
return (V)new ZipFileAttributeView(path, false);
if (type == ZipFileAttributeView.class)
return (V)new ZipFileAttributeView(path, true);
return null;
}
static ZipFileAttributeView get(ZipPath path, String type) {
if (type == null)
throw new NullPointerException();
if (type.equals("basic"))
return new ZipFileAttributeView(path, false);
if (type.equals("zip"))
return new ZipFileAttributeView(path, true);
return null;
}
@Override
public String name() {
return isZipView ? "zip" : "basic";
}
public ZipFileAttributes readAttributes() throws IOException
{
return path.getAttributes();
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime)
throws IOException
{
path.setTimes(lastModifiedTime, lastAccessTime, createTime);
}
void setAttribute(String attribute, Object value)
throws IOException
{
try {
if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime)
setTimes ((FileTime)value, null, null);
return;
} catch (IllegalArgumentException x) {}
throw new UnsupportedOperationException("'" + attribute +
"' is unknown or read-only attribute");
}
public Object getAttribute(String attribute, boolean domap)
throws IOException
{
ZipFileAttributes zfas = readAttributes();
if (!domap) {
try {
return attribute(AttrID.valueOf(attribute), zfas);
} catch (IllegalArgumentException x) {}
return null;
}
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
if ("*".equals(attribute)) {
for (AttrID id : AttrID.values()) {
try {
map.put(id.name(), attribute(id, zfas));
} catch (IllegalArgumentException x) {}
}
} else {
String[] as = attribute.split(",");
for (String a : as) {
try {
map.put(a, attribute(AttrID.valueOf(a), zfas));
} catch (IllegalArgumentException x) {}
}
}
return map;
}
Object attribute(AttrID id, ZipFileAttributes zfas) {
switch (id) {
case size:
return zfas.size();
case creationTime:
return zfas.creationTime();
case lastAccessTime:
return zfas.lastAccessTime();
case lastModifiedTime:
return zfas.lastModifiedTime();
case isDirectory:
return zfas.isDirectory();
case isRegularFile:
return zfas.isRegularFile();
case isSymbolicLink:
return zfas.isSymbolicLink();
case isOther:
return zfas.isOther();
case fileKey:
return zfas.fileKey();
case compressedSize:
if (isZipView)
return zfas.compressedSize();
break;
case crc:
if (isZipView)
return zfas.crc();
break;
case method:
if (isZipView)
return zfas.method();
break;
}
return null;
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.Arrays;
import java.util.Formatter;
import static com.sun.nio.zipfs.ZipUtils.*;
/**
*
* @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
*/
public class ZipFileAttributes implements BasicFileAttributes
{
private final ZipFileSystem.Entry e;
ZipFileAttributes(ZipFileSystem.Entry e) {
this.e = e;
}
///////// basic attributes ///////////
@Override
public FileTime creationTime() {
if (e.ctime != -1)
return FileTime.fromMillis(dosToJavaTime(e.ctime));
return null;
}
@Override
public boolean isDirectory() {
return e.isDir();
}
@Override
public boolean isOther() {
return false;
}
@Override
public boolean isRegularFile() {
return !e.isDir();
}
@Override
public FileTime lastAccessTime() {
if (e.atime != -1)
return FileTime.fromMillis(dosToJavaTime(e.atime));
return null;
}
@Override
public FileTime lastModifiedTime() {
return FileTime.fromMillis(dosToJavaTime(e.mtime));
}
@Override
public long size() {
return e.size;
}
@Override
public boolean isSymbolicLink() {
return false;
}
@Override
public Object fileKey() {
return null;
}
///////// zip entry attributes ///////////
public byte[] name() {
return Arrays.copyOf(e.name, e.name.length);
}
public long compressedSize() {
return e.csize;
}
public long crc() {
return e.crc;
}
public int method() {
return e.method;
}
public byte[] extra() {
if (e.extra != null)
return Arrays.copyOf(e.extra, e.extra.length);
return null;
}
public byte[] comment() {
if (e.comment != null)
return Arrays.copyOf(e.comment, e.comment.length);
return null;
}
public String toString() {
StringBuilder sb = new StringBuilder();
Formatter fm = new Formatter(sb);
fm.format("[/%s]%n", new String(e.name)); // TBD encoding
fm.format(" creationTime : %s%n", creationTime());
if (lastAccessTime() != null)
fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
else
fm.format(" lastAccessTime : null%n");
fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
fm.format(" isRegularFile : %b%n", isRegularFile());
fm.format(" isDirectory : %b%n", isDirectory());
fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
fm.format(" isOther : %b%n", isOther());
fm.format(" fileKey : %s%n", fileKey());
fm.format(" size : %d%n", size());
fm.format(" compressedSize : %d%n", compressedSize());
fm.format(" crc : %x%n", crc());
fm.format(" method : %d%n", method());
fm.close();
return sb.toString();
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileStoreAttributeView;
import java.nio.file.attribute.FileStoreSpaceAttributeView;
import java.nio.file.attribute.FileStoreSpaceAttributes;
import java.nio.file.attribute.Attributes;
import java.nio.file.attribute.BasicFileAttributeView;
import java.util.Formatter;
/*
*
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipFileStore extends FileStore {
private final ZipFileSystem zfs;
ZipFileStore(ZipPath zpath) {
this.zfs = (ZipFileSystem)zpath.getFileSystem();
}
@Override
public String name() {
return zfs.toString() + "/";
}
@Override
public String type() {
return "zipfs";
}
@Override
public boolean isReadOnly() {
return zfs.isReadOnly();
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
return (type == BasicFileAttributeView.class ||
type == ZipFileAttributeView.class);
}
@Override
public boolean supportsFileAttributeView(String name) {
return name.equals("basic") || name.equals("zip");
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
if (type == null)
throw new NullPointerException();
if (type == FileStoreSpaceAttributeView.class)
return (V) new ZipFileStoreAttributeView(this);
return null;
}
@Override
public Object getAttribute(String attribute) throws IOException {
if (attribute.equals("space:totalSpace"))
return new ZipFileStoreAttributeView(this).readAttributes().totalSpace();
if (attribute.equals("space:usableSpace"))
return new ZipFileStoreAttributeView(this).readAttributes().usableSpace();
if (attribute.equals("space:unallocatedSpace"))
return new ZipFileStoreAttributeView(this).readAttributes().unallocatedSpace();
throw new UnsupportedOperationException("does not support the given attribute");
}
private static class ZipFileStoreAttributeView implements FileStoreSpaceAttributeView {
private final ZipFileStore fileStore;
public ZipFileStoreAttributeView(ZipFileStore fileStore) {
this.fileStore = fileStore;
}
@Override
public String name() {
return "space";
}
@Override
public FileStoreSpaceAttributes readAttributes() throws IOException {
final String file = fileStore.name();
Path path = FileSystems.getDefault().getPath(file);
final long size = Attributes.readBasicFileAttributes(path).size();
final FileStore fstore = path.getFileStore();
final FileStoreSpaceAttributes fstoreAttrs =
Attributes.readFileStoreSpaceAttributes(fstore);
return new FileStoreSpaceAttributes() {
public long totalSpace() {
return size;
}
public long usableSpace() {
if (!fstore.isReadOnly())
return fstoreAttrs.usableSpace();
return 0;
}
public long unallocatedSpace() {
if (!fstore.isReadOnly())
return fstoreAttrs.unallocatedSpace();
return 0;
}
public String toString() {
StringBuilder sb = new StringBuilder();
Formatter fm = new Formatter(sb);
fm.format("FileStoreSpaceAttributes[%s]%n", file);
fm.format(" totalSpace: %d%n", totalSpace());
fm.format(" usableSpace: %d%n", usableSpace());
fm.format(" unallocSpace: %d%n", unallocatedSpace());
fm.close();
return sb.toString();
}
};
}
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.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.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.Inflater;
import java.util.zip.Deflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.ZipException;
import java.util.zip.ZipError;
import static java.lang.Boolean.*;
import static com.sun.nio.zipfs.ZipConstants.*;
import static com.sun.nio.zipfs.ZipUtils.*;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.file.StandardCopyOption.*;
/**
* A FileSystem built on a zip file
*
* @author Xueming Shen
*/
public class ZipFileSystem extends FileSystem {
private final ZipFileSystemProvider provider;
private final ZipPath defaultdir;
private boolean readOnly = false;
private final Path zfpath;
private final ZipCoder zc;
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
private final boolean buildDirTree; // build a dir tree for directoryStream ops
private final boolean useTempFile; // use a temp file for newOS, default
// is to use BAOS for better performance
private final boolean createNew; // create a new zip if not exists
ZipFileSystem(ZipFileSystemProvider provider,
Path zfpath,
Map<String, ?> env)
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.nameEncoding = env.containsKey("nameEncoding") ?
(String)env.get("nameEncoding") : "UTF-8";
this.defaultDir = env.containsKey("default.dir") ?
(String)env.get("default.dir") : "/";
if (this.defaultDir.charAt(0) != '/')
throw new IllegalArgumentException("default dir should be absolute");
this.provider = provider;
this.zfpath = zfpath;
if (zfpath.notExists()) {
if (createNew) {
OutputStream os = zfpath.newOutputStream(CREATE_NEW, WRITE);
new END().write(os, 0);
os.close();
} else {
throw new FileSystemNotFoundException(zfpath.toString());
}
}
zfpath.checkAccess(AccessMode.READ); // sm and existence check
try {
zfpath.checkAccess(AccessMode.WRITE);
} catch (AccessDeniedException x) {
this.readOnly = true;
}
this.zc = ZipCoder.get(nameEncoding);
this.defaultdir = new ZipPath(this, getBytes(defaultDir));
initZipFile();
}
@Override
public FileSystemProvider provider() {
return provider;
}
@Override
public String getSeparator() {
return "/";
}
@Override
public boolean isOpen() {
return isOpen;
}
@Override
public boolean isReadOnly() {
return readOnly;
}
private void checkWritable() throws IOException {
if (readOnly)
throw new ReadOnlyFileSystemException();
}
@Override
public Iterable<Path> getRootDirectories() {
ArrayList<Path> pathArr = new ArrayList<>();
pathArr.add(new ZipPath(this, new byte[]{'/'}));
return pathArr;
}
ZipPath getDefaultDir() { // package private
return defaultdir;
}
@Override
public ZipPath getPath(String path) {
if (path.length() == 0)
throw new InvalidPathException(path, "path should not be empty");
return new ZipPath(this, getBytes(path));
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
throw new UnsupportedOperationException();
}
@Override
public WatchService newWatchService() {
throw new UnsupportedOperationException();
}
FileStore getFileStore(ZipPath path) {
return new ZipFileStore(path);
}
@Override
public Iterable<FileStore> getFileStores() {
ArrayList<FileStore> list = new ArrayList<FileStore>(1);
list.add(new ZipFileStore(new ZipPath(this, new byte[]{'/'})));
return list;
}
private static final Set<String> supportedFileAttributeViews =
Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList("basic", "zip")));
@Override
public Set<String> supportedFileAttributeViews() {
return supportedFileAttributeViews;
}
@Override
public String toString() {
return zfpath.toString();
}
Path getZipFile() {
return zfpath;
}
private static final String GLOB_SYNTAX = "glob";
private static final String REGEX_SYNTAX = "regex";
@Override
public PathMatcher getPathMatcher(String syntaxAndInput) {
int pos = syntaxAndInput.indexOf(':');
if (pos <= 0 || pos == syntaxAndInput.length()) {
throw new IllegalArgumentException();
}
String syntax = syntaxAndInput.substring(0, pos);
String input = syntaxAndInput.substring(pos + 1);
String expr;
if (syntax.equals(GLOB_SYNTAX)) {
expr = toRegexPattern(input);
} else {
if (syntax.equals(REGEX_SYNTAX)) {
expr = input;
} else {
throw new UnsupportedOperationException("Syntax '" + syntax +
"' not recognized");
}
}
// return matcher
final Pattern pattern = Pattern.compile(expr);
return new PathMatcher() {
@Override
public boolean matches(Path path) {
return pattern.matcher(path.toString()).matches();
}
};
}
@Override
public void close() throws IOException {
synchronized (lock) {
if (!isOpen)
return;
isOpen = false;
if (!streams.isEmpty()) {
synchronized(streams) {
for (InputStream is: streams)
is.close();
}
}
sync();
ch.close();
}
synchronized (inflaters) {
for (Inflater inf : inflaters)
inf.end();
}
synchronized (deflaters) {
for (Deflater def : deflaters)
def.end();
}
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<IndexNode> 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 {
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());
}
return new ZipFileAttributes(e);
}
}
void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime)
throws IOException
{
checkWritable();
synchronized (lock) {
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());
if (atime != null)
e.atime = javaToDosTime(atime.toMillis());
if (ctime != null)
e.ctime = javaToDosTime(ctime.toMillis());
update(e);
}
}
boolean exists(byte[] path)
throws IOException
{
return getEntry0(path) != null;
}
boolean isDirectory(byte[] path)
throws IOException
{
synchronized (lock) {
if (buildDirTree) {
return getDirs().containsKey(new EntryName(path));
}
Entry e = getEntry0(path);
return (e != null && e.isDir()) || path.length == 0;
}
}
private ZipPath toZipPath(byte[] path) {
// make it absolute
byte[] p = new byte[path.length + 1];
p[0] = '/';
System.arraycopy(path, 0, p, 1, path.length);
return new ZipPath(this, p);
}
// returns the list of child paths of "path"
Iterator<Path> iteratorOf(byte[] path,
DirectoryStream.Filter<? super Path> filter)
throws IOException
{
synchronized (lock) {
if (buildDirTree) {
IndexNode inode = getDirs().get(new EntryName(path));
if (inode == null)
throw new NotDirectoryException(getString(path));
List<Path> list = new ArrayList<Path>();
IndexNode child = inode.child;
while (child != null) {
ZipPath zp = toZipPath(child.name);
if (filter == null || filter.accept(zp))
list.add(zp);
child = child.sibling;
}
return list.iterator();
}
if (!isDirectory(path))
throw new NotDirectoryException(getString(path));
List<Path> list = new ArrayList<Path>();
EntryName[] entries = getEntryNames();
path = toDirectoryPath(path);
for (EntryName en :entries) {
if (!isParentOf(path, en.name)) // is "path" the parent of "name"
continue;
int off = path.length;
while (off < en.name.length) {
if (en.name[off] == '/')
break;
off++;
}
if (off < (en.name.length - 1))
continue;
ZipPath zp = toZipPath(en.name);
if (filter == null || filter.accept(zp))
list.add(zp);
}
return list.iterator();
}
}
void createDirectory(byte[] dir, FileAttribute<?>... attrs)
throws IOException
{
checkWritable();
dir = toDirectoryPath(dir);
synchronized (lock) {
ensureOpen();
// pseudo root dir, or exiting dir
if (dir.length == 0 || exists(dir))
throw new FileAlreadyExistsException(getString(dir));
checkParents(dir);
Entry e = new Entry(dir, Entry.NEW);
e.method = METHOD_STORED; // STORED for dir
update(e);
}
}
void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options)
throws IOException
{
checkWritable();
if (Arrays.equals(src, dst))
return; // do nothing, src and dst are the same
synchronized (lock) {
Entry eSrc = getEntry0(src); // ensureOpen checked
if (eSrc == null)
throw new NoSuchFileException(getString(src));
if (eSrc.isDir()) { // spec says to create dst dir
createDirectory(dst);
return;
}
boolean hasReplace = false;
boolean hasCopyAttrs = false;
for (CopyOption opt : options) {
if (opt == REPLACE_EXISTING)
hasReplace = true;
else if (opt == COPY_ATTRIBUTES)
hasCopyAttrs = true;
}
Entry eDst = getEntry0(dst);
if (eDst != null) {
if (!hasReplace)
throw new FileAlreadyExistsException(getString(dst));
} else {
checkParents(dst);
}
Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry
u.name = dst; // change name
// 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
if (!deletesrc) { // if it's not "rename", just take the data
if (eSrc.bytes != null)
u.bytes = Arrays.copyOf(eSrc.bytes, eSrc.bytes.length);
else if (eSrc.file != null) {
u.file = getTempPathForEntry(null);
eSrc.file.copyTo(u.file, REPLACE_EXISTING);
}
}
}
if (!hasCopyAttrs)
u.mtime = u.atime= u.ctime = javaToDosTime(System.currentTimeMillis());
update(u);
if (deletesrc)
updateDelete(eSrc);
}
}
// Returns an output stream for writing the contents into the specified
// entry.
OutputStream newOutputStream(byte[] path, OpenOption... options)
throws IOException
{
checkWritable();
boolean hasCreateNew = false;
boolean hasCreate = false;
boolean hasAppend = false;
for (OpenOption opt: options) {
if (opt == READ)
throw new IllegalArgumentException("READ not allowed");
if (opt == CREATE_NEW)
hasCreateNew = true;
if (opt == CREATE)
hasCreate = true;
if (opt == APPEND)
hasAppend = true;
}
synchronized (lock) {
Entry e = getEntry0(path);
if (e != null) {
if (e.isDir() || hasCreateNew)
throw new FileAlreadyExistsException(getString(path));
if (hasAppend) {
InputStream is = getInputStream(e);
OutputStream os = getOutputStream(new Entry(e, Entry.NEW));
copyStream(is, os);
is.close();
return os;
}
return getOutputStream(new Entry(e, Entry.NEW));
} else {
if (!hasCreate && !hasCreateNew)
throw new NoSuchFileException(getString(path));
checkParents(path);
return getOutputStream(new Entry(path, Entry.NEW));
}
}
}
// Returns an input stream for reading the contents of the specified
// file entry.
InputStream newInputStream(byte[] path) throws IOException {
synchronized (lock) {
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);
}
}
private void checkOptions(Set<? extends OpenOption> options) {
// check for options of null type and option is an intance of StandardOpenOption
for (OpenOption option : options) {
if (option == null)
throw new NullPointerException();
if (!(option instanceof StandardOpenOption))
throw new IllegalArgumentException();
}
}
// Returns a Writable/ReadByteChannel for now. Might consdier to use
// newFileChannel() instead, which dump the entry data into a regular
// file on the default file system and create a FileChannel on top of
// it.
SeekableByteChannel newByteChannel(byte[] path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
checkOptions(options);
if (options.contains(StandardOpenOption.WRITE) ||
options.contains(StandardOpenOption.APPEND)) {
checkWritable();
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();
}
};
} 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();
}
};
}
}
// Returns a FileChannel of the specified entry.
//
// This implementation creates a temporary file on the default file system,
// copy the entry data into it if the entry exists, and then create a
// FileChannel on top of it.
FileChannel newFileChannel(byte[] path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
checkOptions(options);
final boolean forWrite = (options.contains(StandardOpenOption.WRITE) ||
options.contains(StandardOpenOption.APPEND));
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);
}
}
};
}
// the outstanding input streams that need to be closed
private Set<InputStream> streams =
Collections.synchronizedSet(new HashSet<InputStream>());
// the ex-channel and ex-path that need to close when their outstanding
// input streams are all closed by the obtainers.
private Set<ExChannelCloser> exChClosers = new HashSet<>();
private Set<Path> tmppaths = 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) {
InputStream is = newInputStream(path);
OutputStream os = tmpPath.newOutputStream(WRITE);
try {
copyStream(is, os);
} finally {
is.close();
os.close();
}
}
}
return tmpPath;
}
private void removeTempPathForEntry(Path path) throws IOException {
path.delete();
tmppaths.remove(path);
}
// check if all parents really exit. ZIP spec does not require
// the existence of any "parent directory".
private void checkParents(byte[] path) throws IOException {
while ((path = getParent(path)) != null) {
if (!inodes.containsKey(new EntryName(path)))
throw new NoSuchFileException(getString(path));
}
}
private static byte[] getParent(byte[] path) {
int off = path.length - 1;
if (off > 0 && path[off] == '/') // isDirectory
off--;
while (off > 0 && path[off] != '/') { off--; }
if (off == 0)
return null; // top entry
return Arrays.copyOf(path, off + 1);
}
// If "starter" is the parent directory of "path"
private static boolean isParentOf(byte[] p, byte[] c) {
final int plen = p.length;
if (plen == 0) // root dir
return true;
if (plen >= c.length)
return false;
int n = 0;
while (n < plen) {
if (p[n] != c[n])
return false;
n++;
}
if (p[n - 1] != '/' && (c[n] != '/' || n == c.length - 1))
return false;
return true;
}
///////////////////////////////////////////////////////////////////
private void initZipFile() throws IOException {
ch = zfpath.newByteChannel(READ);
initCEN();
}
private volatile boolean isOpen = true;
private SeekableByteChannel ch; // channel to the zipfile
ByteBuffer 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<EntryName, IndexNode> inodes;
byte[] getBytes(String name) {
return zc.getBytes(name);
}
String getString(byte[] name) {
return zc.toString(name);
}
protected void finalize() throws IOException {
close();
}
private long getDataPos(Entry e) throws IOException {
if (e.locoff == -1) {
Entry e2 = getEntry0(e.name);
if (e2 == null)
throw new ZipException("invalid loc for entry <" + e.name + ">");
e.locoff = e2.locoff;
}
byte[] buf = new byte[LOCHDR];
if (readFullyAt(buf, 0, buf.length, e.locoff) != buf.length)
throw new ZipException("invalid loc for entry <" + e.name + ">");
return locpos + e.locoff + LOCHDR + LOCNAM(buf) + LOCEXT(buf);
}
// Reads len bytes of data from the specified offset into buf.
// Returns the total number of bytes read.
// Each/every byte read from here (except the cen, which is mapped).
private long readFullyAt(byte[] buf, int off, long len, long pos)
throws IOException
{
ByteBuffer bb = ByteBuffer.wrap(buf);
bb.position(off);
bb.limit((int)(off + len));
return readFullyAt(bb, pos);
}
private long readFullyAt(ByteBuffer bb, long pos)
throws IOException
{
synchronized(ch) {
return ch.position(pos).read(bb);
}
}
// Searches for end of central directory (END) header. The contents of
// the END header will be read and placed in endbuf. Returns the file
// position of the END header, otherwise returns -1 if the END header
// was not found or an error occurred.
private END findEND() throws IOException
{
byte[] buf = new byte[READBLOCKSZ];
long ziplen = ch.size();
long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0;
long minPos = minHDR - (buf.length - ENDHDR);
for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR))
{
int off = 0;
if (pos < 0) {
// Pretend there are some NUL bytes before start of file
off = (int)-pos;
Arrays.fill(buf, 0, off, (byte)0);
}
int len = buf.length - off;
if (readFullyAt(buf, off, len, pos + off) != len)
zerror("zip END header not found");
// Now scan the block backwards for END header signature
for (int i = buf.length - ENDHDR; i >= 0; i--) {
if (buf[i+0] == (byte)'P' &&
buf[i+1] == (byte)'K' &&
buf[i+2] == (byte)'\005' &&
buf[i+3] == (byte)'\006' &&
(pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) {
// Found END header
buf = Arrays.copyOfRange(buf, i, i + ENDHDR);
END end = new END();
end.endsub = ENDSUB(buf);
end.centot = ENDTOT(buf);
end.cenlen = ENDSIZ(buf);
end.cenoff = ENDOFF(buf);
end.comlen = ENDCOM(buf);
end.endpos = pos + i;
if (end.cenlen == ZIP64_MINVAL ||
end.cenoff == ZIP64_MINVAL ||
end.centot == ZIP64_MINVAL32)
{
// need to find the zip64 end;
byte[] loc64 = new byte[ZIP64_LOCHDR];
if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
!= loc64.length) {
return end;
}
long end64pos = ZIP64_LOCOFF(loc64);
byte[] end64buf = new byte[ZIP64_ENDHDR];
if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
!= end64buf.length) {
return end;
}
// end64 found, re-calcualte everything.
end.cenlen = ZIP64_ENDSIZ(end64buf);
end.cenoff = ZIP64_ENDOFF(end64buf);
end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g
end.endpos = end64pos;
}
return end;
}
}
}
zerror("zip END header not found");
return null; //make compiler happy
}
// Reads zip file central directory. Returns the file position of first
// CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
// then the error was a zip format error and zip->msg has the error text.
// Always pass in -1 for knownTotal; it's used for a recursive call.
private long initCEN() throws IOException {
end = findEND();
if (end.endpos == 0) {
inodes = new LinkedHashMap<EntryName, IndexNode>(10);
locpos = 0;
return 0; // only END header present
}
if (end.cenlen > end.endpos)
zerror("invalid END header (bad central directory size)");
long cenpos = end.endpos - end.cenlen; // position of CEN table
// Get position of first local file (LOC) header, taking into
// account that there may be a stub prefixed to the zip file.
locpos = cenpos - end.cenoff;
if (locpos < 0)
zerror("invalid END header (bad central directory offset)");
// read in the CEN and END
cen = ByteBuffer.allocate((int)(end.cenlen + ENDHDR));
if (readFullyAt(cen, 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<EntryName, IndexNode>(end.centot + 1);
int pos = 0;
int limit = cen.remaining() - ENDHDR;
int i = 0;
byte[] bBuf = new byte[1024];
while (pos < limit) {
if (CENSIG(cen, pos) != CENSIG)
zerror("invalid CEN header (bad signature)");
int method = CENHOW(cen, pos);
int nlen = CENNAM(cen, pos);
int elen = CENEXT(cen, pos);
int clen = CENCOM(cen, pos);
if ((CENFLG(cen, pos) & 1) != 0)
zerror("invalid CEN header (encrypted entry)");
if (method != METHOD_STORED && method != METHOD_DEFLATED)
zerror("invalid CEN header (bad 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));
// skip ext and comment
cen.position(pos += (CENHDR + nlen + elen + clen));
i++;
}
if (cen.remaining() != ENDHDR) {
zerror("invalid CEN header (bad header size)");
}
dirs = null; // clear the dir map
return cenpos;
}
private void ensureOpen() throws IOException {
if (!isOpen)
throw new ClosedFileSystemException();
}
// Creates a new empty temporary file in the same directory as the
// specified file. A variant of File.createTempFile.
private static 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();
}
////////////////////update & sync //////////////////////////////////////
private boolean hasUpdate = false;
private void updateDelete(Entry e) {
EntryName en = new EntryName(e.name);
inodes.remove(en);
hasUpdate = true;
}
private void update(Entry e) {
EntryName en = new EntryName(e.name);
inodes.put(en, e);
hasUpdate = true;
}
// copy over the whole LOC entry (header if necessary, data and ext) from
// old zip to the new one.
private long copyLOCEntry(Entry e, boolean updateHeader,
OutputStream os,
long written, byte[] buf)
throws IOException
{
long locoff = e.locoff; // where to read
e.locoff = written; // update the e.locoff with new value
// calculate the size need to write out
long size = 0;
// if there is A ext
if ((e.flag & FLAG_DATADESCR) != 0) {
if (e.size >= ZIP64_MINVAL || e.csize >= ZIP64_MINVAL)
size = 24;
else
size = 16;
}
if (updateHeader) { // if we need update the loc header
locoff += LOCHDR + e.nlen + e.elen; // skip header
size += e.csize;
written = e.writeLOC(os) + size;
} else {
size += LOCHDR + e.nlen + e.elen + e.csize;
written = size;
}
int n;
while (size > 0 &&
(n = (int)readFullyAt(buf, 0, buf.length, locoff)) != -1)
{
if (size < n)
n = (int)size;
os.write(buf, 0, n);
size -= n;
locoff += n;
}
return written;
}
// sync the zip file system, if there is any udpate
private void sync() throws IOException {
assert Thread.holdsLock(this);
// check ex-closer
if (!exChClosers.isEmpty()) {
for (ExChannelCloser ecc : exChClosers) {
if (ecc.streams.isEmpty()) {
ecc.ch.close();
ecc.path.delete();
exChClosers.remove(ecc);
}
}
}
if (!hasUpdate)
return;
Path tmpFile = createTempFileInSameDirectoryAs(zfpath);
OutputStream os = tmpFile.newOutputStream(WRITE);
ArrayList<Entry> elist = new ArrayList<>(inodes.size());
long written = 0;
byte[] buf = new byte[8192];
Entry e = null;
// write loc
for (IndexNode inode : inodes.values()) {
if (inode instanceof Entry) { // an updated inode
e = (Entry)inode;
try {
if (e.type == Entry.COPY) {
// entry copy: the only thing changed is the "name"
// and "nlen" in LOC header, so we udpate/rewrite the
// LOC in new file and simply copy the rest (data and
// ext) without enflating/deflating from the old zip
// file LOC entry.
written += copyLOCEntry(e, true, os, written, buf);
} else { // NEW or FILECH
e.locoff = written;
written += e.writeLOC(os); // write loc header
if (e.bytes != null) { // in-memory, deflated
os.write(e.bytes); // already
written += e.bytes.length;
} else if (e.file != null) { // tmp file
InputStream is = e.file.newInputStream();
int n;
if (e.type == Entry.NEW) { // deflated already
while ((n = is.read(buf)) != -1) {
os.write(buf, 0, n);
written += n;
}
} else if (e.type == Entry.FILECH) {
// the data are not deflated, use ZEOS
OutputStream os2 = new EntryOutputStream(e, os);
while ((n = is.read(buf)) != -1) {
os2.write(buf, 0, n);
}
os2.close();
written += e.csize;
if ((e.flag & FLAG_DATADESCR) != 0)
written += e.writeEXT(os);
}
is.close();
e.file.delete();
tmppaths.remove(e.file);
} else {
// dir, 0-length data
}
}
elist.add(e);
} catch (IOException x) {
x.printStackTrace(); // skip any in-accurate entry
}
} else { // unchanged inode
e = Entry.readCEN(cen, inode.pos);
try {
written += copyLOCEntry(e, false, os, written, buf);
elist.add(e);
} catch (IOException x) {
x.printStackTrace(); // skip any wrong entry
}
}
}
// now write back the cen and end table
end.cenoff = written;
for (Entry entry : elist) {
written += entry.writeCEN(os);
}
end.centot = elist.size();
end.cenlen = written - end.cenoff;
end.write(os, written);
os.close();
if (!streams.isEmpty()) {
// There are outstanding input streams open on existing "ch",
// so, don't close the "cha" and delete the "file for now, let
// the "ex-channel-closer" to handle them
ExChannelCloser ecc = new ExChannelCloser(
createTempFileInSameDirectoryAs(zfpath),
ch,
streams);
zfpath.moveTo(ecc.path, REPLACE_EXISTING);
exChClosers.add(ecc);
streams = Collections.synchronizedSet(new HashSet<InputStream>());
} else {
ch.close();
zfpath.delete();
}
tmpFile.moveTo(zfpath, REPLACE_EXISTING);
hasUpdate = false; // clear
if (isOpen) {
ch = zfpath.newByteChannel(READ); // re-fresh "ch" and "cen"
initCEN();
}
//System.out.println("->sync() done!");
}
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);
}
}
// Test if the "name" a parent directory of any entry (dir empty)
boolean isAncestor(byte[] name) {
for (Map.Entry<EntryName, IndexNode> entry : inodes.entrySet()) {
byte[] ename = entry.getKey().name;
if (isParentOf(name, ename))
return true;
}
return false;
}
public void deleteFile(byte[] path, boolean failIfNotExists)
throws IOException
{
checkWritable();
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);
}
}
}
private static void copyStream(InputStream is, OutputStream os)
throws IOException
{
byte[] copyBuf = new byte[8192];
int n;
while ((n = is.read(copyBuf)) != -1) {
os.write(copyBuf, 0, n);
}
}
// Returns an out stream for either
// (1) writing the contents of a new entry, if the entry exits, or
// (2) updating/replacing the contents of the specified existing entry.
private OutputStream getOutputStream(Entry e) throws IOException {
ensureOpen();
if (e.mtime == -1)
e.mtime = javaToDosTime(System.currentTimeMillis());
if (e.method == -1)
e.method = METHOD_DEFLATED; // TBD: use default method
// store size, compressed size, and crc-32 in LOC header
e.flag = 0;
if (zc.isUTF8())
e.flag |= FLAG_EFS;
OutputStream os;
if (useTempFile) {
e.file = getTempPathForEntry(null);
os = e.file.newOutputStream(WRITE);
} else {
os = new ByteArrayOutputStream((e.size > 0)? (int)e.size : 8192);
}
return new EntryOutputStream(e, os);
}
private InputStream getInputStream(Entry e)
throws IOException
{
InputStream eis = null;
if (e.type == Entry.NEW) {
if (e.bytes != null)
eis = new ByteArrayInputStream(e.bytes);
else if (e.file != null)
eis = e.file.newInputStream();
else
throw new ZipException("update entry data is missing");
} else if (e.type == Entry.FILECH) {
// FILECH result is un-compressed.
eis = e.file.newInputStream();
// TBD: wrap to hook close()
// streams.add(eis);
return eis;
} else { // untouced CEN or COPY
eis = new EntryInputStream(e, ch);
}
if (e.method == METHOD_DEFLATED) {
// MORE: Compute good size for inflater stream:
long bufSize = e.size + 2; // Inflater likes a bit of slack
if (bufSize > 65536)
bufSize = 8192;
final long size = e.size;;
eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) {
private boolean isClosed = false;
public void close() throws IOException {
if (!isClosed) {
releaseInflater(inf);
this.in.close();
isClosed = true;
}
}
// Override fill() method to provide an extra "dummy" byte
// at the end of the input stream. This is required when
// using the "nowrap" Inflater option. (it appears the new
// zlib in 7 does not need it, but keep it for now)
protected void fill() throws IOException {
if (eof) {
throw new EOFException(
"Unexpected end of ZLIB input stream");
}
len = this.in.read(buf, 0, buf.length);
if (len == -1) {
buf[0] = 0;
len = 1;
eof = true;
}
inf.setInput(buf, 0, len);
}
private boolean eof;
public int available() throws IOException {
if (isClosed)
return 0;
long avail = size - inf.getBytesWritten();
return avail > (long) Integer.MAX_VALUE ?
Integer.MAX_VALUE : (int) avail;
}
};
} else if (e.method != METHOD_STORED) {
throw new ZipException("invalid compression method");
}
streams.add(eis);
return eis;
}
// Inner class implementing the input stream used to read
// a (possibly compressed) zip file entry.
private class EntryInputStream extends InputStream {
private 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
EntryInputStream(Entry e, SeekableByteChannel zfch)
throws IOException
{
this.zfch = zfch;
rem = e.csize;
size = e.size;
pos = getDataPos(e);
}
public int read(byte b[], int off, int len) throws IOException {
ensureOpen();
if (rem == 0) {
return -1;
}
if (len <= 0) {
return 0;
}
if (len > rem) {
len = (int) rem;
}
// readFullyAt()
long n = 0;
ByteBuffer bb = ByteBuffer.wrap(b);
bb.position(off);
bb.limit(off + len);
synchronized(zfch) {
n = zfch.position(pos).read(bb);
}
if (n > 0) {
pos += n;
rem -= n;
}
if (rem == 0) {
close();
}
return (int)n;
}
public int read() throws IOException {
byte[] b = new byte[1];
if (read(b, 0, 1) == 1) {
return b[0] & 0xff;
} else {
return -1;
}
}
public long skip(long n) throws IOException {
ensureOpen();
if (n > rem)
n = rem;
pos += n;
rem -= n;
if (rem == 0) {
close();
}
return n;
}
public int available() {
return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
}
public long size() {
return size;
}
public void close() {
rem = 0;
streams.remove(this);
}
}
class EntryOutputStream extends DeflaterOutputStream
{
private CRC32 crc;
private Entry e;
private long written;
EntryOutputStream(Entry e, OutputStream os)
throws IOException
{
super(os, getDeflater());
if (e == null)
throw new NullPointerException("Zip entry is null");
this.e = e;
crc = new CRC32();
}
@Override
public void write(byte b[], int off, int len) throws IOException {
if (e.type != Entry.FILECH) // only from sync
ensureOpen();
if (off < 0 || len < 0 || off > b.length - len) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
switch (e.method) {
case METHOD_DEFLATED:
super.write(b, off, len);
break;
case METHOD_STORED:
written += len;
out.write(b, off, len);
break;
default:
throw new ZipException("invalid compression method");
}
crc.update(b, off, len);
}
@Override
public void close() throws IOException {
// TBD ensureOpen();
switch (e.method) {
case METHOD_DEFLATED:
finish();
e.size = def.getBytesRead();
e.csize = def.getBytesWritten();
e.crc = crc.getValue();
break;
case METHOD_STORED:
// we already know that both e.size and e.csize are the same
e.size = e.csize = written;
e.crc = crc.getValue();
break;
default:
throw new ZipException("invalid compression method");
}
//crc.reset();
if (out instanceof ByteArrayOutputStream)
e.bytes = ((ByteArrayOutputStream)out).toByteArray();
if (e.type == Entry.FILECH) {
releaseDeflater(def);
return;
}
super.close();
releaseDeflater(def);
update(e);
}
}
private 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<Inflater> inflaters = new ArrayList<>();
// Gets an inflater from the list of available inflaters or allocates
// a new one.
private Inflater getInflater() {
synchronized (inflaters) {
int size = inflaters.size();
if (size > 0) {
Inflater inf = (Inflater)inflaters.remove(size - 1);
return inf;
} else {
return new Inflater(true);
}
}
}
// Releases the specified inflater to the list of available inflaters.
private void releaseInflater(Inflater inf) {
synchronized (inflaters) {
if (inflaters.size() < MAX_FLATER) {
inf.reset();
inflaters.add(inf);
} else {
inf.end();
}
}
}
// List of available Deflater objects for compression
private List<Deflater> deflaters = new ArrayList<>();
// Gets an deflater from the list of available deflaters or allocates
// a new one.
private Deflater getDeflater() {
synchronized (deflaters) {
int size = deflaters.size();
if (size > 0) {
Deflater def = (Deflater)deflaters.remove(size - 1);
return def;
} else {
return new Deflater(Deflater.DEFAULT_COMPRESSION, true);
}
}
}
// Releases the specified inflater to the list of available inflaters.
private void releaseDeflater(Deflater def) {
synchronized (deflaters) {
if (inflaters.size() < MAX_FLATER) {
def.reset();
deflaters.add(def);
} else {
def.end();
}
}
}
// End of central directory record
static class END {
int disknum;
int sdisknum;
int endsub; // endsub
int centot; // 4 bytes
long cenlen; // 4 bytes
long cenoff; // 4 bytes
int comlen; // comment length
byte[] comment;
/* members of Zip64 end of central directory locator */
int diskNum;
long endpos;
int disktot;
void write(OutputStream os, long offset) throws IOException {
boolean hasZip64 = false;
long xlen = cenlen;
long xoff = cenoff;
if (xlen >= ZIP64_MINVAL) {
xlen = ZIP64_MINVAL;
hasZip64 = true;
}
if (xoff >= ZIP64_MINVAL) {
xoff = ZIP64_MINVAL;
hasZip64 = true;
}
int count = centot;
if (count >= ZIP64_MINVAL32) {
count = ZIP64_MINVAL32;
hasZip64 = true;
}
if (hasZip64) {
long off64 = offset;
//zip64 end of central directory record
writeInt(os, ZIP64_ENDSIG); // zip64 END record signature
writeLong(os, ZIP64_ENDHDR - 12); // size of zip64 end
writeShort(os, 45); // version made by
writeShort(os, 45); // version needed to extract
writeInt(os, 0); // number of this disk
writeInt(os, 0); // central directory start disk
writeLong(os, centot); // number of directory entires on disk
writeLong(os, centot); // number of directory entires
writeLong(os, cenlen); // length of central directory
writeLong(os, cenoff); // offset of central directory
//zip64 end of central directory locator
writeInt(os, ZIP64_LOCSIG); // zip64 END locator signature
writeInt(os, 0); // zip64 END start disk
writeLong(os, off64); // offset of zip64 END
writeInt(os, 1); // total number of disks (?)
}
writeInt(os, ENDSIG); // END record signature
writeShort(os, 0); // number of this disk
writeShort(os, 0); // central directory start disk
writeShort(os, count); // number of directory entries on disk
writeShort(os, count); // total number of directory entries
writeInt(os, xlen); // length of central directory
writeInt(os, xoff); // offset of central directory
if (comment != null) { // zip file comment
writeShort(os, comment.length);
writeBytes(os, comment);
} else {
writeShort(os, 0);
}
}
}
// wrapper for the byte[] name
static class EntryName {
byte[] name;
int hashcode; // node is hashable/hashed by its name
public EntryName (byte[] name) {
name(name);
}
void name(byte[] name) {
this.name = name;
this.hashcode = Arrays.hashCode(name);
}
public boolean equals(Object other) {
if (!(other instanceof EntryName))
return false;
return Arrays.equals(name, ((EntryName)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
}
static class Entry extends IndexNode {
static final int CEN = 1; // entry read from cen
static final int NEW = 2; // updated contents in bytes or file
static final int FILECH = 3; // fch update in "file"
static final int COPY = 4; // copy of a CEN entry
byte[] bytes; // updated content bytes
Path file; // use tmp file to store bytes;
int type = CEN; // default is the entry read from cen
// entry attributes
int version;
int flag;
int method = -1; // compression method
long mtime = -1; // last modification time (in DOS time)
long atime = -1; // last access time
long ctime = -1; // create time
long crc = -1; // crc-32 of entry data
long csize = -1; // compressed size of entry data
long size = -1; // uncompressed size of entry data
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;
}
Entry(byte[] name, int type) {
this(name);
this.type = type;
}
Entry (byte[] name, Path file, int type) {
this(name, type);
this.file = file;
this.method = METHOD_STORED;
}
Entry(Entry e) {
this.version = e.version;
this.name = e.name; // copyOf?
this.nlen = e.nlen;
this.ctime = e.ctime;
this.atime = e.atime;
this.mtime = e.mtime;
this.crc = e.crc;
this.size = e.size;
this.csize = e.csize;
this.method = e.method;
this.extra = (e.extra == null)?
null:Arrays.copyOf(e.extra, e.extra.length);
this.elen = e.elen;
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;;
}
Entry (Entry e, int type) {
this(e);
this.type = type;
}
boolean isDir() {
return name != null &&
(name.length == 0 ||
name[name.length - 1] == '/');
}
int version() throws ZipException {
if (method == METHOD_DEFLATED)
return 20;
else if (method == METHOD_STORED)
return 10;
throw new ZipException("unsupported compression method");
}
///////////////////// CEN //////////////////////
static Entry readCEN(ByteBuffer cen, int pos) throws IOException
{
return new Entry().cen(cen, pos);
}
private Entry cen(ByteBuffer cen, int pos) throws IOException
{
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);
crc = CENCRC(cen, pos);
csize = CENSIZ(cen, pos);
size = CENLEN(cen, pos);
nlen = CENNAM(cen, pos);
elen = CENEXT(cen, pos);
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);
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);
}
}
}
if (clen > 0) {
comment = new byte[clen];
cen.get(comment);
}
return this;
}
int writeCEN(OutputStream os) throws IOException
{
int written = CENHDR;
int version0 = version();
long csize0 = csize;
long size0 = size;
long locoff0 = locoff;
int e64len = 0;
// confirm size/length
nlen = (name != null) ? name.length : 0;
elen = (extra != null) ? extra.length : 0;
clen = (comment != null) ? comment.length : 0;
boolean hasZip64 = false;
if (csize >= ZIP64_MINVAL) {
csize0 = ZIP64_MINVAL;
e64len += 8; // csize(8)
hasZip64 = true;
}
if (size >= ZIP64_MINVAL) {
size0 = ZIP64_MINVAL; // size(8)
e64len += 8;
hasZip64 = true;
}
if (locoff >= ZIP64_MINVAL) {
locoff0 = ZIP64_MINVAL;
e64len += 8; // offset(8)
hasZip64 = true;
}
writeInt(os, CENSIG); // CEN header signature
if (hasZip64) {
writeShort(os, 45); // ver 4.5 for zip64
writeShort(os, 45);
} else {
writeShort(os, version0); // version made by
writeShort(os, version0); // version needed to extract
}
writeShort(os, flag); // general purpose bit flag
writeShort(os, method); // compression method
writeInt(os, mtime); // last modification time
writeInt(os, crc); // crc-32
writeInt(os, csize0); // compressed size
writeInt(os, size0); // uncompressed size
writeShort(os, name.length);
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 {
writeShort(os, 0);
}
writeShort(os, 0); // starting disk number
writeShort(os, 0); // internal file attributes (unused)
writeInt(os, 0); // external file attributes (unused)
writeInt(os, locoff0); // relative offset of local header
writeBytes(os, name);
if (hasZip64) {
writeShort(os, EXTID_ZIP64);// Zip64 extra
writeShort(os, e64len);
if (size0 == ZIP64_MINVAL)
writeLong(os, size);
if (csize0 == ZIP64_MINVAL)
writeLong(os, csize);
if (locoff0 == ZIP64_MINVAL)
writeLong(os, locoff);
}
if (extra != null) {
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);
}
///////////////////// LOC //////////////////////
static Entry readLOC(ZipFileSystem zf, long pos)
throws IOException
{
return readLOC(zf, pos, new byte[1024]);
}
static Entry readLOC(ZipFileSystem zf, long pos, byte[] buf)
throws IOException
{
return new Entry().loc(zf, pos, buf);
}
Entry loc(ZipFileSystem zf, long pos, byte[] buf)
throws IOException
{
assert (buf.length >= LOCHDR);
if (zf.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR) {
throw new ZipException("loc: reading failed");
}
if (LOCSIG(buf) != LOCSIG) {
throw new ZipException("loc: wrong sig ->"
+ Long.toString(LOCSIG(buf), 16));
}
startPos = pos;
version = LOCVER(buf);
flag = LOCFLG(buf);
method = LOCHOW(buf);
mtime = LOCTIM(buf);
crc = LOCCRC(buf);
csize = LOCSIZ(buf);
size = LOCLEN(buf);
nlen = LOCNAM(buf);
elen = LOCEXT(buf);
name = new byte[nlen];
if (zf.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)
!= elen) {
throw new ZipException("loc: ext reading failed");
}
}
pos += (LOCHDR + nlen + elen);
if ((flag & FLAG_DATADESCR) != 0) {
// Data Descriptor
Entry e = zf.getEntry0(name); // get the size/csize from cen
if (e == null)
throw new ZipException("loc: name not found in cen");
size = e.size;
csize = e.csize;
pos += (method == METHOD_STORED ? size : csize);
if (size >= ZIP64_MINVAL || csize >= ZIP64_MINVAL)
pos += 24;
else
pos += 16;
} else {
boolean hasZip64 = false;
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);
hasZip64 = true;
break;
}
off += (sz + 4);
}
}
pos += (method == METHOD_STORED ? size : csize);
}
endPos = pos;
return this;
}
int writeLOC(OutputStream os)
throws IOException
{
writeInt(os, LOCSIG); // LOC header signature
int version = version();
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
// store size, uncompressed size, and crc-32 in data descriptor
// immediately following compressed entry data
writeInt(os, 0);
writeInt(os, 0);
writeInt(os, 0);
} else {
if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
hasZip64 = true;
writeShort(os, 45); // ver 4.5 for zip64
} else {
writeShort(os, version()); // version needed to extract
}
writeShort(os, flag); // general purpose bit flag
writeShort(os, method); // compression method
writeInt(os, mtime); // last modification time
writeInt(os, crc); // crc-32
if (hasZip64) {
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
}
}
writeShort(os, name.length);
writeShort(os, elen + (hasZip64 ? 20 : 0));
writeBytes(os, name);
if (hasZip64) {
// TBD: should we update extra directory?
writeShort(os, EXTID_ZIP64);
writeShort(os, 16);
writeLong(os, size);
writeLong(os, csize);
}
if (extra != null) {
writeBytes(os, extra);
}
return LOCHDR + name.length + elen + (hasZip64 ? 20 : 0);
}
// Data Descriptior
int writeEXT(OutputStream os)
throws IOException
{
writeInt(os, EXTSIG); // EXT header signature
writeInt(os, crc); // crc-32
if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
writeLong(os, csize);
writeLong(os, size);
return 24;
} else {
writeInt(os, csize); // compressed size
writeInt(os, size); // uncompressed size
return 16;
}
}
// read NTFS, UNIX and ZIP64 data from cen.extra
void readExtra() {
if (extra == null)
return;
int elen = extra.length;
int off = 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;
if (pos + sz > elen) // invalid data
break;
switch (tag) {
case EXTID_ZIP64 :
if (size == ZIP64_MINVAL) {
if (pos + 8 > elen) // invalid zip64 extra
break; // fields, just skip
size = LL(extra, pos);
pos += 8;
}
if (csize == ZIP64_MINVAL) {
if (pos + 8 > elen)
break;
csize = LL(extra, pos);
pos += 8;
}
if (locoff == ZIP64_MINVAL) {
if (pos + 8 > elen)
break;
locoff = LL(extra, pos);
pos += 8;
}
break;
case EXTID_NTFS:
pos += 4; // reserved 4 bytes
if (SH(extra, pos) != 0x0001)
break;
if (SH(extra, pos + 2) != 24)
break;
mtime = LL(extra, pos + 4);
atime = LL(extra, pos + 12);
ctime = LL(extra, pos + 20);
break;
case EXTID_UNIX:
atime = LG(extra, pos);
mtime = LG(extra, pos + 4);
break;
default: // unknow
}
off += sz;
}
}
}
private static class ExChannelCloser {
Path path;
SeekableByteChannel ch;
Set<InputStream> streams;
ExChannelCloser(Path path,
SeekableByteChannel ch,
Set<InputStream> streams)
{
this.path = path;
this.ch = ch;
this.streams = streams;
}
}
// ZIP directory has two issues:
// (1) ZIP spec does not require the ZIP file to include
// directory entry
// (2) all entries are not stored/organized in a "tree"
// structure.
// A possible solution is to build the node tree ourself as
// implemented below.
private HashMap<EntryName, IndexNode> dirs;
private IndexNode root;
private IndexNode addToDir(EntryName child) {
IndexNode cinode = dirs.get(child);
if (cinode != null)
return cinode;
byte[] cname = child.name;
byte[] pname = getParent(cname);
IndexNode pinode;
if (pname != null)
pinode = addToDir(new EntryName(pname));
else
pinode = root;
cinode = inodes.get(child);
if (cname[cname.length -1] != '/') { // not a dir
cinode.sibling = pinode.child;
pinode.child = cinode;
return null;
}
cinode = dirs.get(child);
if (cinode == null) // pseudo directry entry
cinode = new IndexNode(cname, -1);
cinode.sibling = pinode.child;
pinode.child = cinode;
dirs.put(child, cinode);
return cinode;
}
private HashMap<EntryName, IndexNode> getDirs()
throws IOException
{
if (hasUpdate)
sync();
if (dirs != null)
return dirs;
dirs = new HashMap<EntryName, IndexNode>();
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;
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.FileRef;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.ProviderMismatchException;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.spi.FileSystemProvider;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
*
* @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal
*/
public class ZipFileSystemProvider extends FileSystemProvider {
private final Map<Path, ZipFileSystem> filesystems = new HashMap<>();
public ZipFileSystemProvider() {}
@Override
public String getScheme() {
return "zip";
}
protected Path uriToPath(URI uri) {
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) {
throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'");
}
try {
return Paths.get(new URI("file", uri.getHost(), uri.getPath(), null))
.toAbsolutePath();
} catch (URISyntaxException e) {
throw new AssertionError(e); //never thrown
}
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env)
throws IOException
{
return newFileSystem(uriToPath(uri), env);
}
@Override
public FileSystem newFileSystem(FileRef file, Map<String, ?> env)
throws IOException
{
if (!(file instanceof Path))
throw new UnsupportedOperationException();
Path path = (Path)file;
if (!path.toUri().getScheme().equalsIgnoreCase("file")) {
throw new UnsupportedOperationException();
}
return newFileSystem(path, env);
}
private FileSystem newFileSystem(Path path, Map<String, ?> env)
throws IOException
{
synchronized(filesystems) {
if (filesystems.containsKey(path))
throw new FileSystemAlreadyExistsException();
ZipFileSystem zipfs = new ZipFileSystem(this, path, env);
filesystems.put(path, zipfs);
return zipfs;
}
}
@Override
public Path getPath(URI uri) {
FileSystem fs = getFileSystem(uri);
String fragment = uri.getFragment();
if (fragment == null) {
throw new IllegalArgumentException("URI: "
+ uri
+ " does not contain path fragment ex. zip:///c:/foo.zip#/BAR");
}
return fs.getPath(fragment);
}
@Override
public FileChannel newFileChannel(Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
if (path == null)
throw new NullPointerException("path is null");
if (path instanceof ZipPath)
return ((ZipPath)path).newFileChannel(options, attrs);
throw new ProviderMismatchException();
}
@Override
public FileSystem getFileSystem(URI uri) {
synchronized (filesystems) {
ZipFileSystem zipfs = filesystems.get(uriToPath(uri));
if (zipfs == null)
throw new FileSystemNotFoundException();
return zipfs;
}
}
void removeFileSystem(Path zfpath) {
synchronized (filesystems) {
filesystems.remove(zfpath);
}
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.io.PrintStream;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import com.sun.nio.zipfs.ZipFileSystem.Entry;
import static com.sun.nio.zipfs.ZipConstants.*;
import static com.sun.nio.zipfs.ZipUtils.*;
/**
* Print the loc and cen tables of the ZIP file
*
* @author Xueming Shen
*/
public class ZipInfo {
public static void main(String[] args) throws Throwable {
if (args.length < 2) {
print("Usage: java ZipInfo [cen|loc] zfname");
} else {
Map<String, ?> env = Collections.emptyMap();
ZipFileSystem zfs = (ZipFileSystem)(new ZipFileSystemProvider()
.newFileSystem(Paths.get(args[1]), env));
long pos = 0;
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<ZipFileSystem.IndexNode> 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++;
}
}
zfs.close();
}
}
static void print(String fmt, Object... objs) {
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 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");
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.io.File;
import java.io.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.*;
/**
*
* @author Xueming Shen, Rajendra Gutupalli,Jaya Hangal
*/
public class ZipPath extends Path {
private final ZipFileSystem zfs;
private final byte[] path;
private volatile int[] offsets;
private int hashcode = 0; // cached hashcode (created lazily)
ZipPath(ZipFileSystem zfs, byte[] path) {
this(zfs, path, false);
}
ZipPath(ZipFileSystem zfs, byte[] path, boolean normalized)
{
this.zfs = zfs;
if (normalized)
this.path = path;
else
this.path = normalize(path);
}
@Override
public ZipPath getRoot() {
if (this.isAbsolute())
return new ZipPath(zfs, new byte[]{path[0]});
else
return null;
}
@Override
public Path getName() {
initOffsets();
int count = offsets.length;
if (count == 0)
return null; // no elements so no name
if (count == 1 && path[0] != '/')
return this;
int lastOffset = offsets[count-1];
int len = path.length - lastOffset;
byte[] result = new byte[len];
System.arraycopy(path, lastOffset, result, 0, len);
return new ZipPath(zfs, result);
}
@Override
public ZipPath getParent() {
initOffsets();
int count = offsets.length;
if (count == 0) // no elements so no parent
return null;
int len = offsets[count-1] - 1;
if (len <= 0) // parent is root only (may be null)
return getRoot();
byte[] result = new byte[len];
System.arraycopy(path, 0, result, 0, len);
return new ZipPath(zfs, result);
}
@Override
public int getNameCount() {
initOffsets();
return offsets.length;
}
@Override
public ZipPath getName(int index) {
initOffsets();
if (index < 0 || index >= offsets.length)
throw new IllegalArgumentException();
int begin = offsets[index];
int len;
if (index == (offsets.length-1))
len = path.length - begin;
else
len = offsets[index+1] - begin - 1;
// construct result
byte[] result = new byte[len];
System.arraycopy(path, begin, result, 0, len);
return new ZipPath(zfs, result);
}
@Override
public ZipPath subpath(int beginIndex, int endIndex) {
initOffsets();
if (beginIndex < 0 ||
beginIndex >= offsets.length ||
endIndex > offsets.length ||
beginIndex >= endIndex)
throw new IllegalArgumentException();
// starting offset and length
int begin = offsets[beginIndex];
int len;
if (endIndex == offsets.length)
len = path.length - begin;
else
len = offsets[endIndex] - begin - 1;
// construct result
byte[] result = new byte[len];
System.arraycopy(path, begin, result, 0, len);
return new ZipPath(zfs, result);
}
@Override
public ZipPath toRealPath(boolean resolveLinks) throws IOException {
ZipPath realPath = new ZipPath(zfs, getResolvedPath());
realPath.checkAccess();
return realPath;
}
@Override
public boolean isHidden() {
return false;
}
@Override
public ZipPath toAbsolutePath() {
if (isAbsolute()) {
return this;
} else {
//add / bofore the existing path
byte[] defaultdir = zfs.getDefaultDir().path;
int defaultlen = defaultdir.length;
boolean endsWith = (defaultdir[defaultlen - 1] == '/');
byte[] t = null;
if (endsWith)
t = new byte[defaultlen + path.length];
else
t = new byte[defaultlen + 1 + path.length];
System.arraycopy(defaultdir, 0, t, 0, defaultlen);
if (!endsWith)
t[defaultlen++] = '/';
System.arraycopy(path, 0, t, defaultlen, path.length);
return new ZipPath(zfs, t, true); // normalized
}
}
@Override
public URI toUri() {
String zfPath = zfs.toString();
if (File.separatorChar == '\\') // replace all separators by '/'
zfPath = "/" + zfPath.replace("\\", "/");
try {
return new URI("zip", "",
zfPath,
zfs.getString(toAbsolutePath().path));
} catch (Exception ex) {
throw new AssertionError(ex);
}
}
private boolean equalsNameAt(ZipPath other, int index) {
int mbegin = offsets[index];
int mlen = 0;
if (index == (offsets.length-1))
mlen = path.length - mbegin;
else
mlen = offsets[index + 1] - mbegin - 1;
int obegin = other.offsets[index];
int olen = 0;
if (index == (other.offsets.length - 1))
olen = other.path.length - obegin;
else
olen = other.offsets[index + 1] - obegin - 1;
if (mlen != olen)
return false;
int n = 0;
while(n < mlen) {
if (path[mbegin + n] != other.path[obegin + n])
return false;
n++;
}
return true;
}
@Override
public Path relativize(Path other) {
final ZipPath o = checkPath(other);
if (o.equals(this))
return null;
if (/* this.getFileSystem() != o.getFileSystem() || */
this.isAbsolute() != o.isAbsolute()) {
throw new IllegalArgumentException();
}
int mc = this.getNameCount();
int oc = o.getNameCount();
int n = Math.min(mc, oc);
int i = 0;
while (i < n) {
if (!equalsNameAt(o, i))
break;
i++;
}
int dotdots = mc - i;
int len = dotdots * 3 - 1;
if (i < oc)
len += (o.path.length - o.offsets[i] + 1);
byte[] result = new byte[len];
int pos = 0;
while (dotdots > 0) {
result[pos++] = (byte)'.';
result[pos++] = (byte)'.';
if (pos < len) // no tailing slash at the end
result[pos++] = (byte)'/';
dotdots--;
}
if (i < oc)
System.arraycopy(o.path, o.offsets[i],
result, pos,
o.path.length - o.offsets[i]);
return new ZipPath(getFileSystem(), result);
}
@Override
public ZipFileSystem getFileSystem() {
return zfs;
}
@Override
public boolean isAbsolute() {
return (this.path[0] == '/');
}
@Override
public ZipPath resolve(Path other) {
if (other == null)
return this;
final ZipPath o = checkPath(other);
if (o.isAbsolute())
return o;
byte[] resolved = null;
if (this.path[path.length - 1] == '/') {
resolved = new byte[path.length + o.path.length];
System.arraycopy(path, 0, resolved, 0, path.length);
System.arraycopy(o.path, 0, resolved, path.length, o.path.length);
} else {
resolved = new byte[path.length + 1 + o.path.length];
System.arraycopy(path, 0, resolved, 0, path.length);
resolved[path.length] = '/';
System.arraycopy(o.path, 0, resolved, path.length + 1, o.path.length);
}
return new ZipPath(zfs, resolved);
}
@Override
public ZipPath resolve(String other) {
return resolve(getFileSystem().getPath(other));
}
@Override
public boolean startsWith(Path other) {
final ZipPath o = checkPath(other);
if (o.isAbsolute() != this.isAbsolute())
return false;
final int oCount = o.getNameCount();
if (getNameCount() < oCount)
return false;
for (int i = 0; i < oCount; i++) {
if (!o.getName(i).equals(getName(i)))
return false;
}
return true;
}
@Override
public boolean endsWith(Path other) {
final ZipPath o = checkPath(other);
if (o.isAbsolute())
return this.isAbsolute() ? this.equals(o) : false;
int i = o.getNameCount();
int j = this.getNameCount();
if (j < i)
return false;
for (--i, --j; i >= 0; i--, j--) {
if (!o.getName(i).equals(this.getName(j)))
return false;
}
return true;
}
@Override
public Path normalize() {
byte[] resolved = getResolved();
if (resolved == path) // no change
return this;
if (resolved.length == 0)
return null;
return new ZipPath(zfs, resolved, true);
}
private ZipPath checkPath(Path path) {
if (path == null)
throw new NullPointerException();
if (!(path instanceof ZipPath))
throw new ProviderMismatchException();
return (ZipPath) path;
}
// create offset list if not already created
private void initOffsets() {
if (offsets == null) {
int count, index;
// count names
count = 0;
index = 0;
while (index < path.length) {
byte c = path[index++];
if (c != '/') {
count++;
while (index < path.length && path[index] != '/')
index++;
}
}
// populate offsets
int[] result = new int[count];
count = 0;
index = 0;
while (index < path.length) {
byte c = path[index];
if (c == '/') {
index++;
} else {
result[count++] = index++;
while (index < path.length && path[index] != '/')
index++;
}
}
synchronized (this) {
if (offsets == null)
offsets = result;
}
}
}
// resolved path for locating zip entry inside the zip file,
// the result path does not contain ./ and .. components
private volatile byte[] resolved = null;
byte[] getResolvedPath() {
byte[] r = resolved;
if (r == null) {
if (isAbsolute())
r = getResolved();
else
r = toAbsolutePath().getResolvedPath();
if (r[0] == '/')
r = Arrays.copyOfRange(r, 1, r.length);
resolved = r;
}
return resolved;
}
// removes redundant slashs, replace "\" to zip separator "/"
// and check for invalid characters
private byte[] normalize(byte[] path) {
if (path.length == 0)
return path;
byte prevC = 0;
for (int i = 0; i < path.length; i++) {
byte c = path[i];
if (c == '\\')
return normalize(path, i);
if (c == (byte)'/' && prevC == '/')
return normalize(path, i - 1);
if (c == '\u0000')
throw new InvalidPathException(zfs.getString(path),
"Path: nul character not allowed");
prevC = c;
}
return path;
}
private byte[] normalize(byte[] path, int off) {
byte[] to = new byte[path.length];
int n = 0;
while (n < off) {
to[n] = path[n];
n++;
}
int m = n;
byte prevC = 0;
while (n < path.length) {
byte c = path[n++];
if (c == (byte)'\\')
c = (byte)'/';
if (c == (byte)'/' && prevC == (byte)'/')
continue;
if (c == '\u0000')
throw new InvalidPathException(zfs.getString(path),
"Path: nul character not allowed");
to[m++] = c;
prevC = c;
}
if (m > 1 && to[m - 1] == '/')
m--;
return (m == to.length)? to : Arrays.copyOf(to, m);
}
// Remove DotSlash(./) and resolve DotDot (..) components
private byte[] getResolved() {
if (path.length == 0)
return path;
for (int i = 0; i < path.length; i++) {
byte c = path[i];
if (c == (byte)'.')
return resolve0();
}
return path;
}
// TBD: performance, avoid initOffsets
private byte[] resolve0() {
byte[] to = new byte[path.length];
int nc = getNameCount();
int[] lastM = new int[nc];
int lastMOff = -1;
int m = 0;
for (int i = 0; i < nc; i++) {
int n = offsets[i];
int len = (i == offsets.length - 1)?
(path.length - n):(offsets[i + 1] - n - 1);
if (len == 1 && path[n] == (byte)'.')
continue;
if (len == 2 && path[n] == '.' && path[n + 1] == '.') {
if (lastMOff >= 0) {
m = lastM[lastMOff--]; // retreat
continue;
}
if (path[0] == '/') { // "/../xyz" skip
if (m == 0)
to[m++] = '/';
} else { // "../xyz" -> "../xyz"
if (m != 0 && to[m-1] != '/')
to[m++] = '/';
while (len-- > 0)
to[m++] = path[n++];
}
continue;
}
if (m == 0 && path[0] == '/' || // absolute path
m != 0 && to[m-1] != '/') { // not the first name
to[m++] = '/';
}
lastM[++lastMOff] = m;
while (len-- > 0)
to[m++] = path[n++];
}
if (m > 1 && to[m - 1] == '/')
m--;
return (m == to.length)? to : Arrays.copyOf(to, m);
}
@Override
public String toString() {
return zfs.getString(path);
}
@Override
public int hashCode() {
int h = hashcode;
if (h == 0)
hashcode = h = Arrays.hashCode(path);
return h;
}
@Override
public boolean equals(Object obj) {
return obj != null &&
obj instanceof ZipPath &&
this.zfs == ((ZipPath)obj).zfs &&
compareTo((Path) obj) == 0;
}
@Override
public int compareTo(Path other) {
final ZipPath o = checkPath(other);
int len1 = this.path.length;
int len2 = o.path.length;
int n = Math.min(len1, len2);
byte v1[] = this.path;
byte v2[] = o.path;
int k = 0;
while (k < n) {
int c1 = v1[k] & 0xff;
int c2 = v2[k] & 0xff;
if (c1 != c2)
return c1 - c2;
k++;
}
return len1 - len2;
}
@Override
public Path createSymbolicLink(
Path target, FileAttribute<?>... attrs) throws IOException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public Path createLink(
Path existing) throws IOException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public Path readSymbolicLink() throws IOException {
throw new UnsupportedOperationException("Not supported.");
}
@Override
public Path createDirectory(FileAttribute<?>... attrs)
throws IOException
{
zfs.createDirectory(getResolvedPath(), attrs);
return this;
}
public final Path createFile(FileAttribute<?>... attrs)
throws IOException
{
OutputStream os = newOutputStream(CREATE_NEW, WRITE);
try {
os.close();
} catch (IOException x) {}
return this;
}
@Override
public InputStream newInputStream(OpenOption... options)
throws IOException {
if (options.length > 0) {
for (OpenOption opt : options) {
if (opt != READ)
throw new UnsupportedOperationException("'" + opt + "' not allowed");
}
}
return zfs.newInputStream(getResolvedPath());
}
private static final DirectoryStream.Filter<Path> acceptAllFilter =
new DirectoryStream.Filter<Path>() {
@Override public boolean accept(Path entry) { return true; }
};
@Override
public final DirectoryStream<Path> newDirectoryStream() throws IOException {
return newDirectoryStream(acceptAllFilter);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
throws IOException
{
return new ZipDirectoryStream(this, filter);
}
@Override
public final DirectoryStream<Path> newDirectoryStream(String glob)
throws IOException
{
// avoid creating a matcher if all entries are required.
if (glob.equals("*"))
return newDirectoryStream();
// create a matcher and return a filter that uses it.
final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob);
DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
@Override
public boolean accept(Path entry) {
return matcher.matches(entry.getName());
}
};
return newDirectoryStream(filter);
}
@Override
public final void delete() throws IOException {
zfs.deleteFile(getResolvedPath(), true);
}
@Override
public final void deleteIfExists() throws IOException {
zfs.deleteFile(getResolvedPath(), false);
}
ZipFileAttributes getAttributes() throws IOException
{
ZipFileAttributes zfas = zfs.getFileAttributes(getResolvedPath());
if (zfas == null)
throw new NoSuchFileException(toString());
return zfas;
}
@Override
@SuppressWarnings("unchecked")
public <V extends FileAttributeView> V getFileAttributeView(Class<V> type,
LinkOption... options)
{
return (V)ZipFileAttributeView.get(this, type);
}
@Override
public void setAttribute(String attribute,
Object value,
LinkOption... options)
throws IOException
{
String type = null;
String attr = null;
int colonPos = attribute.indexOf(':');
if (colonPos == -1) {
type = "basic";
attr = attribute;
} else {
type = attribute.substring(0, colonPos++);
attr = attribute.substring(colonPos);
}
ZipFileAttributeView view = ZipFileAttributeView.get(this, type);
if (view == null)
throw new UnsupportedOperationException("view <" + view + "> is not supported");
view.setAttribute(attr, value);
}
void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
throws IOException
{
zfs.setTimes(getResolvedPath(), mtime, atime, ctime);
}
private Object getAttributesImpl(String attribute, boolean domap)
throws IOException
{
String view = null;
String attr = null;
int colonPos = attribute.indexOf(':');
if (colonPos == -1) {
view = "basic";
attr = attribute;
} else {
view = attribute.substring(0, colonPos++);
attr = attribute.substring(colonPos);
}
ZipFileAttributeView zfv = ZipFileAttributeView.get(this, view);
if (zfv == null) {
throw new UnsupportedOperationException("view not supported");
}
return zfv.getAttribute(attr, domap);
}
@Override
public Object getAttribute(String attribute, LinkOption... options)
throws IOException
{
return getAttributesImpl(attribute, false);
}
@Override
public Map<String,?> readAttributes(String attribute, LinkOption... options)
throws IOException
{
return (Map<String, ?>)getAttributesImpl(attribute, true);
}
@Override
public FileStore getFileStore() throws IOException {
// each ZipFileSystem only has one root (as requested for now)
if (exists())
return zfs.getFileStore(this);
throw new NoSuchFileException(zfs.getString(path));
}
@Override
public boolean isSameFile(Path other) throws IOException {
if (other == null ||
this.getFileSystem() != other.getFileSystem())
return false;
this.checkAccess();
other.checkAccess();
return Arrays.equals(this.getResolvedPath(),
((ZipPath)other).getResolvedPath());
}
public WatchKey register(
WatchService watcher,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers) {
if (watcher == null || events == null || modifiers == null) {
throw new NullPointerException();
}
throw new UnsupportedOperationException();
}
@Override
public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
return register(watcher, events, new WatchEvent.Modifier[0]);
}
@Override
public Iterator<Path> iterator() {
return new Iterator<Path>() {
private int i = 0;
@Override
public boolean hasNext() {
return (i < getNameCount());
}
@Override
public Path next() {
if (i < getNameCount()) {
Path result = getName(i);
i++;
return result;
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new ReadOnlyFileSystemException();
}
};
}
@Override
public SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return zfs.newByteChannel(getResolvedPath(), options, attrs);
}
FileChannel newFileChannel(Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return zfs.newFileChannel(getResolvedPath(), options, attrs);
}
@Override
public SeekableByteChannel newByteChannel(OpenOption... options)
throws IOException {
Set<OpenOption> set = new HashSet<OpenOption>(options.length);
Collections.addAll(set, options);
return newByteChannel(set);
}
@Override
public void checkAccess(AccessMode... modes) throws IOException {
boolean w = false;
boolean x = false;
for (AccessMode mode : modes) {
switch (mode) {
case READ:
break;
case WRITE:
w = true;
break;
case EXECUTE:
x = true;
break;
default:
throw new UnsupportedOperationException();
}
}
ZipFileAttributes attrs = zfs.getFileAttributes(getResolvedPath());
if (attrs == null && (path.length != 1 || path[0] != '/'))
throw new NoSuchFileException(toString());
if (w) {
if (zfs.isReadOnly())
throw new AccessDeniedException(toString());
}
if (x)
throw new AccessDeniedException(toString());
}
@Override
public boolean exists() {
if (path.length == 1 && path[0] == '/')
return true;
try {
return zfs.exists(getResolvedPath());
} catch (IOException x) {}
return false;
}
@Override
public boolean notExists() {
return !exists();
}
@Override
public OutputStream newOutputStream(OpenOption... options)
throws IOException
{
if (options.length == 0)
return zfs.newOutputStream(getResolvedPath(),
CREATE_NEW, WRITE);
return zfs.newOutputStream(getResolvedPath(), options);
}
@Override
public Path moveTo(Path target, CopyOption... options)
throws IOException
{
if (this.zfs.provider() == target.getFileSystem().provider() &&
this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile()))
{
zfs.copyFile(true,
getResolvedPath(),
((ZipPath)target).getResolvedPath(),
options);
} else {
copyToTarget(target, options);
delete();
}
return target;
}
@Override
public Path copyTo(Path target, CopyOption... options)
throws IOException
{
if (this.zfs.provider() == target.getFileSystem().provider() &&
this.zfs.getZipFile().isSameFile(((ZipPath)target).zfs.getZipFile()))
{
zfs.copyFile(false,
getResolvedPath(),
((ZipPath)target).getResolvedPath(),
options);
} else {
copyToTarget(target, options);
}
return target;
}
private void copyToTarget(Path target, CopyOption... options)
throws IOException
{
boolean replaceExisting = false;
boolean copyAttrs = false;
for (CopyOption opt : options) {
if (opt == REPLACE_EXISTING)
replaceExisting = true;
else if (opt == COPY_ATTRIBUTES)
copyAttrs = false;
}
// attributes of source file
ZipFileAttributes zfas = getAttributes();
// check if target exists
boolean exists;
if (replaceExisting) {
try {
target.deleteIfExists();
exists = false;
} catch (DirectoryNotEmptyException x) {
exists = true;
}
} else {
exists = target.exists();
}
if (exists)
throw new FileAlreadyExistsException(target.toString());
if (zfas.isDirectory()) {
// create directory or file
target.createDirectory();
} else {
InputStream is = zfs.newInputStream(getResolvedPath());
try {
OutputStream os = target.newOutputStream();
try {
byte[] buf = new byte[8192];
int n = 0;
while ((n = is.read(buf)) != -1) {
os.write(buf, 0, n);
}
} finally {
os.close();
}
} finally {
is.close();
}
}
if (copyAttrs) {
BasicFileAttributeView view =
target.getFileAttributeView(BasicFileAttributeView.class);
try {
view.setTimes(zfas.lastModifiedTime(), null, null);
} catch (IOException x) {
// rollback?
try {
target.delete();
} catch (IOException ignore) { }
throw x;
}
}
}
}
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.sun.nio.zipfs;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.regex.PatternSyntaxException;
/**
*
* @author Xueming Shen
*/
class ZipUtils {
/*
* Writes a 16-bit short to the output stream in little-endian byte order.
*/
public static void writeShort(OutputStream os, int v) throws IOException {
os.write((v >>> 0) & 0xff);
os.write((v >>> 8) & 0xff);
}
/*
* Writes a 32-bit int to the output stream in little-endian byte order.
*/
public static void writeInt(OutputStream os, long v) throws IOException {
os.write((int)((v >>> 0) & 0xff));
os.write((int)((v >>> 8) & 0xff));
os.write((int)((v >>> 16) & 0xff));
os.write((int)((v >>> 24) & 0xff));
}
/*
* Writes a 64-bit int to the output stream in little-endian byte order.
*/
public static void writeLong(OutputStream os, long v) throws IOException {
os.write((int)((v >>> 0) & 0xff));
os.write((int)((v >>> 8) & 0xff));
os.write((int)((v >>> 16) & 0xff));
os.write((int)((v >>> 24) & 0xff));
os.write((int)((v >>> 32) & 0xff));
os.write((int)((v >>> 40) & 0xff));
os.write((int)((v >>> 48) & 0xff));
os.write((int)((v >>> 56) & 0xff));
}
/*
* Writes an array of bytes to the output stream.
*/
public static void writeBytes(OutputStream os, byte[] b)
throws IOException
{
os.write(b, 0, b.length);
}
/*
* Writes an array of bytes to the output stream.
*/
public static void writeBytes(OutputStream os, byte[] b, int off, int len)
throws IOException
{
os.write(b, off, len);
}
/*
* Append a slash at the end, if it does not have one yet
*/
public static byte[] toDirectoryPath(byte[] dir) {
if (dir.length != 0 && dir[dir.length - 1] != '/') {
dir = Arrays.copyOf(dir, dir.length + 1);
dir[dir.length - 1] = '/';
}
return dir;
}
/*
* Converts DOS time to Java time (number of milliseconds since epoch).
*/
public static long dosToJavaTime(long dtime) {
Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
(int)(((dtime >> 21) & 0x0f) - 1),
(int)((dtime >> 16) & 0x1f),
(int)((dtime >> 11) & 0x1f),
(int)((dtime >> 5) & 0x3f),
(int)((dtime << 1) & 0x3e));
return d.getTime();
}
/*
* Converts Java time to DOS time.
*/
public static long javaToDosTime(long time) {
Date d = new Date(time);
int year = d.getYear() + 1900;
if (year < 1980) {
return (1 << 21) | (1 << 16);
}
return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
d.getSeconds() >> 1;
}
private static final String regexMetaChars = ".^$+{[]|()";
private static final String globMetaChars = "\\*?[{";
private static boolean isRegexMeta(char c) {
return regexMetaChars.indexOf(c) != -1;
}
private static boolean isGlobMeta(char c) {
return globMetaChars.indexOf(c) != -1;
}
private static char EOL = 0; //TBD
private static char next(String glob, int i) {
if (i < glob.length()) {
return glob.charAt(i);
}
return EOL;
}
/*
* Creates a regex pattern from the given glob expression.
*
* @throws PatternSyntaxException
*/
public static String toRegexPattern(String globPattern) {
boolean inGroup = false;
StringBuilder regex = new StringBuilder("^");
int i = 0;
while (i < globPattern.length()) {
char c = globPattern.charAt(i++);
switch (c) {
case '\\':
// escape special characters
if (i == globPattern.length()) {
throw new PatternSyntaxException("No character to escape",
globPattern, i - 1);
}
char next = globPattern.charAt(i++);
if (isGlobMeta(next) || isRegexMeta(next)) {
regex.append('\\');
}
regex.append(next);
break;
case '/':
regex.append(c);
break;
case '[':
// don't match name separator in class
regex.append("[[^/]&&[");
if (next(globPattern, i) == '^') {
// escape the regex negation char if it appears
regex.append("\\^");
i++;
} else {
// negation
if (next(globPattern, i) == '!') {
regex.append('^');
i++;
}
// hyphen allowed at start
if (next(globPattern, i) == '-') {
regex.append('-');
i++;
}
}
boolean hasRangeStart = false;
char last = 0;
while (i < globPattern.length()) {
c = globPattern.charAt(i++);
if (c == ']') {
break;
}
if (c == '/') {
throw new PatternSyntaxException("Explicit 'name separator' in class",
globPattern, i - 1);
}
// TBD: how to specify ']' in a class?
if (c == '\\' || c == '[' ||
c == '&' && next(globPattern, i) == '&') {
// escape '\', '[' or "&&" for regex class
regex.append('\\');
}
regex.append(c);
if (c == '-') {
if (!hasRangeStart) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 1);
}
if ((c = next(globPattern, i++)) == EOL || c == ']') {
break;
}
if (c < last) {
throw new PatternSyntaxException("Invalid range",
globPattern, i - 3);
}
regex.append(c);
hasRangeStart = false;
} else {
hasRangeStart = true;
last = c;
}
}
if (c != ']') {
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
}
regex.append("]]");
break;
case '{':
if (inGroup) {
throw new PatternSyntaxException("Cannot nest groups",
globPattern, i - 1);
}
regex.append("(?:(?:");
inGroup = true;
break;
case '}':
if (inGroup) {
regex.append("))");
inGroup = false;
} else {
regex.append('}');
}
break;
case ',':
if (inGroup) {
regex.append(")|(?:");
} else {
regex.append(',');
}
break;
case '*':
if (next(globPattern, i) == '*') {
// crosses directory boundaries
regex.append(".*");
i++;
} else {
// within directory boundary
regex.append("[^/]*");
}
break;
case '?':
regex.append("[^/]");
break;
default:
if (isRegexMeta(c)) {
regex.append('\\');
}
regex.append(c);
}
}
if (inGroup) {
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
}
return regex.append('$').toString();
}
}
/*
* Copyright (c) 2009, 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.
*/
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileSystemProvider;
import java.util.*;
import java.net.URI;
import java.io.IOException;
/**
* Basic test for zip provider
*/
public class Basic {
public static void main(String[] args) throws Exception {
Path zipfile = Paths.get(args[0]);
// Test: zip should should be returned in provider list
boolean found = false;
for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
if (provider.getScheme().equalsIgnoreCase("zip")) {
found = true;
break;
}
}
if (!found)
throw new RuntimeException("'zip' provider not installed");
// Test: FileSystems#newFileSystem(FileRef)
Map<String,?> env = new HashMap<String,Object>();
FileSystems.newFileSystem(zipfile, env, null).close();
// Test: FileSystems#newFileSystem(URI)
URI uri = URI.create("zip" + zipfile.toUri().toString().substring(4));
FileSystem fs = FileSystems.newFileSystem(uri, env, null);
// Test: exercise toUri method
String expected = uri.toString() + "#/foo";
String actual = fs.getPath("/foo").toUri().toString();
if (!actual.equals(expected)) {
throw new RuntimeException("toUri returned '" + actual +
"', expected '" + expected + "'");
}
// Test: exercise directory iterator and retrieval of basic attributes
Files.walkFileTree(fs.getPath("/"), new FileTreePrinter());
// Test: DirectoryStream
found = false;
DirectoryStream<Path> stream = fs.getPath("/").newDirectoryStream();
try {
for (Path entry: stream) {
found = entry.toString().equals("/META-INF/");
if (found) break;
}
} finally {
stream.close();
}
if (!found)
throw new RuntimeException("Expected file not found");
// Test: copy file from zip file to current (scratch) directory
Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider");
if (source.exists()) {
Path target = Paths.get(source.getName().toString());
source.copyTo(target, StandardCopyOption.REPLACE_EXISTING);
try {
long s1 = Attributes.readBasicFileAttributes(source).size();
long s2 = Attributes.readBasicFileAttributes(target).size();
if (s2 != s1)
throw new RuntimeException("target size != source size");
} finally {
target.delete();
}
}
// Test: FileStore
FileStore store = fs.getPath("/").getFileStore();
if (!store.supportsFileAttributeView("basic"))
throw new RuntimeException("BasicFileAttributeView should be supported");
// Test: ClosedFileSystemException
fs.close();
if (fs.isOpen())
throw new RuntimeException("FileSystem should be closed");
try {
fs.getPath("/missing").checkAccess(AccessMode.READ);
} catch (ClosedFileSystemException x) { }
}
// FileVisitor that pretty prints a file tree
static class FileTreePrinter extends SimpleFileVisitor<Path> {
private int indent = 0;
private void indent() {
StringBuilder sb = new StringBuilder(indent);
for (int i=0; i<indent; i++) sb.append(" ");
System.out.print(sb);
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs)
{
if (dir.getName() != null) {
indent();
System.out.println(dir.getName() + "/");
indent++;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs)
{
indent();
System.out.print(file.getName());
if (attrs.isRegularFile())
System.out.format(" (%d)", attrs.size());
System.out.println();
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException
{
if (exc != null)
super.postVisitDirectory(dir, exc);
if (dir.getName() != null)
indent--;
return FileVisitResult.CONTINUE;
}
}
}
/*
* Copyright (c) 2009, 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.
*/
import java.nio.file.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
/**
* Tests path operations for zip provider.
*/
public class PathOps {
static final java.io.PrintStream out = System.out;
static FileSystem fs;
private String input;
private Path path;
private Exception exc;
private PathOps(String s) {
out.println();
input = s;
try {
path = fs.getPath(s);
out.format("%s -> %s", s, path);
} catch (Exception x) {
exc = x;
out.format("%s -> %s", s, x);
}
out.println();
}
Path path() {
return path;
}
void fail() {
throw new RuntimeException("PathOps failed");
}
void checkPath() {
if (path == null) {
throw new InternalError("path is null");
}
}
void check(Object result, String expected) {
out.format("\tExpected: %s\n", expected);
out.format("\tActual: %s\n", result);
if (result == null) {
if (expected == null) return;
} else {
// compare string representations
if (expected != null && result.toString().equals(expected.toString()))
return;
}
fail();
}
void check(Object result, boolean expected) {
check(result, Boolean.toString(expected));
}
PathOps root(String expected) {
out.println("check root");
checkPath();
check(path.getRoot(), expected);
return this;
}
PathOps parent(String expected) {
out.println("check parent");
checkPath();
check(path.getParent(), expected);
return this;
}
PathOps name(String expected) {
out.println("check name");
checkPath();
check(path.getName(), expected);
return this;
}
PathOps element(int index, String expected) {
out.format("check element %d\n", index);
checkPath();
check(path.getName(index), expected);
return this;
}
PathOps subpath(int startIndex, int endIndex, String expected) {
out.format("test subpath(%d,%d)\n", startIndex, endIndex);
checkPath();
check(path.subpath(startIndex, endIndex), expected);
return this;
}
PathOps starts(String prefix) {
out.format("test startsWith with %s\n", prefix);
checkPath();
Path s = fs.getPath(prefix);
check(path.startsWith(s), true);
return this;
}
PathOps notStarts(String prefix) {
out.format("test not startsWith with %s\n", prefix);
checkPath();
Path s = fs.getPath(prefix);
check(path.startsWith(s), false);
return this;
}
PathOps ends(String suffix) {
out.format("test endsWith %s\n", suffix);
checkPath();
Path s = fs.getPath(suffix);
check(path.endsWith(s), true);
return this;
}
PathOps notEnds(String suffix) {
out.format("test not endsWith %s\n", suffix);
checkPath();
Path s = fs.getPath(suffix);
check(path.endsWith(s), false);
return this;
}
PathOps absolute() {
out.println("check path is absolute");
checkPath();
check(path.isAbsolute(), true);
return this;
}
PathOps notAbsolute() {
out.println("check path is not absolute");
checkPath();
check(path.isAbsolute(), false);
return this;
}
PathOps resolve(String other, String expected) {
out.format("test resolve %s\n", other);
checkPath();
check(path.resolve(other), expected);
return this;
}
PathOps relativize(String other, String expected) {
out.format("test relativize %s\n", other);
checkPath();
Path that = fs.getPath(other);
check(path.relativize(that), expected);
return this;
}
PathOps normalize(String expected) {
out.println("check normalized path");
checkPath();
check(path.normalize(), expected);
return this;
}
PathOps string(String expected) {
out.println("check string representation");
checkPath();
check(path, expected);
return this;
}
PathOps invalid() {
if (!(exc instanceof InvalidPathException)) {
out.println("InvalidPathException not thrown as expected");
fail();
}
return this;
}
static PathOps test(String s) {
return new PathOps(s);
}
// -- PathOpss --
static void header(String s) {
out.println();
out.println();
out.println("-- " + s + " --");
}
static void doPathOpTests() {
header("Path operations");
// all components
test("/a/b/c")
.root("/")
.parent("/a/b")
.name("c");
// root component only
test("/")
.root("/")
.parent(null)
.name(null);
// no root component
test("a/b")
.root(null)
.parent("a")
.name("b");
// name component only
test("foo")
.root(null)
.parent(null)
.name("foo");
// startsWith
test("/")
.starts("/")
.notStarts("/foo");
test("/foo")
.starts("/")
.starts("/foo")
.notStarts("/f");
test("/foo/bar")
.starts("/")
.starts("/foo")
.starts("/foo/bar")
.notStarts("/f")
.notStarts("foo")
.notStarts("foo/bar");
test("foo")
.starts("foo")
.notStarts("f");
test("foo/bar")
.starts("foo")
.starts("foo/bar")
.notStarts("f")
.notStarts("/foo")
.notStarts("/foo/bar");
// endsWith
test("/")
.ends("/")
.notEnds("foo")
.notEnds("/foo");
test("/foo")
.ends("foo")
.ends("/foo")
.notEnds("/");
test("/foo/bar")
.ends("bar")
.ends("foo/bar")
.ends("/foo/bar")
.notEnds("/bar");
test("foo")
.ends("foo");
test("foo/bar")
.ends("bar")
.ends("foo/bar");
// elements
test("a/b/c")
.element(0,"a")
.element(1,"b")
.element(2,"c");
// isAbsolute
test("/")
.absolute();
test("/tmp")
.absolute();
test("tmp")
.notAbsolute();
// resolve
test("/tmp")
.resolve("foo", "/tmp/foo")
.resolve("/foo", "/foo");
test("tmp")
.resolve("foo", "tmp/foo")
.resolve("/foo", "/foo");
// relativize
test("/a/b/c")
.relativize("/a/b/c", null)
.relativize("/a/b/c/d/e", "d/e")
.relativize("/a/x", "../../x");
// normalize
test("/")
.normalize("/");
test("foo")
.normalize("foo");
test("/foo")
.normalize("/foo");
test(".")
.normalize(null);
test("..")
.normalize("..");
test("/..")
.normalize("/");
test("/../..")
.normalize("/");
test("foo/.")
.normalize("foo");
test("./foo")
.normalize("foo");
test("foo/..")
.normalize(null);
test("../foo")
.normalize("../foo");
test("../../foo")
.normalize("../../foo");
test("foo/bar/..")
.normalize("foo");
test("foo/bar/gus/../..")
.normalize("foo");
test("/foo/bar/gus/../..")
.normalize("/foo");
// invalid
test("foo\u0000bar")
.invalid();
test("\u0000foo")
.invalid();
test("bar\u0000")
.invalid();
test("//foo\u0000bar")
.invalid();
test("//\u0000foo")
.invalid();
test("//bar\u0000")
.invalid();
// normalization
test("//foo//bar")
.string("/foo/bar")
.root("/")
.parent("/foo")
.name("bar");
}
static void npes() {
header("NullPointerException");
Path path = fs.getPath("foo");
try {
path.resolve((String)null);
throw new RuntimeException("NullPointerException not thrown");
} catch (NullPointerException npe) {
}
try {
path.relativize(null);
throw new RuntimeException("NullPointerException not thrown");
} catch (NullPointerException npe) {
}
try {
path.compareTo(null);
throw new RuntimeException("NullPointerException not thrown");
} catch (NullPointerException npe) {
}
try {
path.startsWith(null);
throw new RuntimeException("NullPointerException not thrown");
} catch (NullPointerException npe) {
}
try {
path.endsWith(null);
throw new RuntimeException("NullPointerException not thrown");
} catch (NullPointerException npe) {
}
}
public static void main(String[] args) throws Throwable {
Path zipfile = Paths.get(args[0]);
Map<String,?> env = new HashMap<String,Object>();
fs = FileSystems.newFileSystem(zipfile, env, null);
npes();
doPathOpTests();
fs.close();
}
}
/*
* 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.
*/
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.net.*;
import java.util.*;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.file.StandardCopyOption.*;
/*
* Tests various zipfs operations.
*/
public class ZipFSTester {
public static void main(String[] args) throws Throwable {
FileSystem fs = null;
try {
fs = newZipFileSystem(Paths.get(args[0]), new HashMap<String, Object>());
test(fs);
test2(fs); // more tests
} finally {
if (fs != null)
fs.close();
}
}
static void test(FileSystem fs)
throws Exception
{
Random rdm = new Random();
// clone a fs and test on it
Path tmpfsPath = getTempPath();
Map<String, Object> env = new HashMap<String, Object>();
env.put("createNew", true);
FileSystem fs0 = newZipFileSystem(tmpfsPath, env);
z2zcopy(fs, fs0, "/", 0);
fs0.close(); // sync to file
fs = newZipFileSystem(tmpfsPath, new HashMap<String, Object>());
try {
// prepare a src
Path src = getTempPath();
String tmpName = src.toString();
OutputStream os = src.newOutputStream();
byte[] bits = new byte[12345];
rdm.nextBytes(bits);
os.write(bits);
os.close();
// copyin
Path dst = getPathWithParents(fs, tmpName);
src.copyTo(dst);
checkEqual(src, dst);
// copy
Path dst2 = getPathWithParents(fs, "/xyz" + rdm.nextInt(100) +
"/efg" + rdm.nextInt(100) + "/foo.class");
dst.copyTo(dst2);
//dst.moveTo(dst2);
checkEqual(src, dst2);
// delete
dst.delete();
if (dst.exists())
throw new RuntimeException("Failed!");
// moveout
Path dst3 = Paths.get(tmpName + "_Tmp");
dst2.moveTo(dst3);
checkEqual(src, dst3);
// delete
if (dst2.exists())
throw new RuntimeException("Failed!");
dst3.delete();
if (dst3.exists())
throw new RuntimeException("Failed!");
// newInputStream on dir
Path parent = dst2.getParent();
try {
parent.newInputStream();
throw new RuntimeException("Failed");
} catch (FileSystemException e) {
e.printStackTrace(); // expected fse
}
// rmdirs
try {
rmdirs(parent);
} catch (IOException x) {
x.printStackTrace();
}
// newFileChannel() copy in, out and verify via fch
fchCopy(src, dst); // in
checkEqual(src, dst);
Path tmp = Paths.get(tmpName + "_Tmp");
fchCopy(dst, tmp); // out
checkEqual(src, tmp);
tmp.delete();
// test channels
channel(fs, dst);
dst.delete();
src.delete();
} finally {
if (fs != null)
fs.close();
if (tmpfsPath.exists())
tmpfsPath.delete();
}
}
static void test2(FileSystem fs) throws Exception {
Path fs1Path = getTempPath();
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<String, Object> env = new HashMap<String, Object>();
env.put("createNew", true);
FileSystem fs0 = newZipFileSystem(fs1Path, env);
final FileSystem fs2 = newZipFileSystem(fs2Path, env);
final FileSystem fs3 = newZipFileSystem(fs3Path, env);
System.out.println("copy src: fs -> fs0...");
z2zcopy(fs, fs0, "/", 0); // copy fs -> fs1
fs0.close(); // dump to file
System.out.println("open fs0 as fs1");
env = new HashMap<String, Object>();
final FileSystem fs1 = newZipFileSystem(fs1Path, env);
System.out.println("listing...");
final ArrayList<String> files = new ArrayList<>();
final ArrayList<String> dirs = new ArrayList<>();
list(fs1.getPath("/"), files, dirs);
Thread t0 = new Thread(new Runnable() {
public void run() {
List<String> list = new ArrayList<>(dirs);
Collections.shuffle(list);
for (String path : list) {
try {
z2zcopy(fs1, fs2, path, 0);
} catch (Exception x) {
x.printStackTrace();
}
}
}
});
Thread t1 = new Thread(new Runnable() {
public void run() {
List<String> list = new ArrayList<>(dirs);
Collections.shuffle(list);
for (String path : list) {
try {
z2zcopy(fs1, fs2, path, 1);
} catch (Exception x) {
x.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
List<String> list = new ArrayList<>(dirs);
Collections.shuffle(list);
for (String path : list) {
try {
z2zcopy(fs1, fs2, path, 2);
} catch (Exception x) {
x.printStackTrace();
}
}
}
});
Thread t3 = new Thread(new Runnable() {
public void run() {
List<String> list = new ArrayList<>(files);
Collections.shuffle(list);
while (!list.isEmpty()) {
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
String path = itr.next();
try {
if (fs2.getPath(path).exists()) {
z2zmove(fs2, fs3, path);
itr.remove();
}
} catch (FileAlreadyExistsException x){
itr.remove();
} catch (Exception x) {
x.printStackTrace();
}
}
}
}
});
System.out.println("copying/removing...");
t0.start(); t1.start(); t2.start(); t3.start();
t0.join(); t1.join(); t2.join(); t3.join();
System.out.println("closing: fs1, fs2");
fs1.close();
fs2.close();
int failed = 0;
System.out.println("checkEqual: fs vs fs3");
for (String path : files) {
try {
checkEqual(fs.getPath(path), fs3.getPath(path));
} catch (IOException x) {
//x.printStackTrace();
failed++;
}
}
System.out.println("closing: fs3");
fs3.close();
System.out.println("opening: fs3 as fs4");
FileSystem fs4 = newZipFileSystem(fs3Path, env);
ArrayList<String> files2 = new ArrayList<>();
ArrayList<String> dirs2 = new ArrayList<>();
list(fs4.getPath("/"), files2, dirs2);
System.out.println("checkEqual: fs vs fs4");
for (String path : files2) {
checkEqual(fs.getPath(path), fs4.getPath(path));
}
System.out.println("walking: fs4");
walk(fs4.getPath("/"));
System.out.println("closing: fs4");
fs4.close();
System.out.printf("failed=%d%n", failed);
fs1Path.delete();
fs2Path.delete();
fs3Path.delete();
}
private static FileSystem newZipFileSystem(Path path, Map<String, ?> env)
throws IOException
{
return FileSystems.newFileSystem(
URI.create("zip" +
path.toUri().toString().substring(4)),
env,
null);
}
private static Path getTempPath() throws IOException
{
File tmp = File.createTempFile("testzipfs_", "zip");
tmp.delete(); // we need a clean path, no file
return tmp.toPath();
}
private static void list(Path path, List<String> files, List<String> dirs )
throws IOException
{
if (Attributes.readBasicFileAttributes(path).isDirectory()) {
DirectoryStream<Path> ds = path.newDirectoryStream();
for (Path child : ds)
list(child, files, dirs);
ds.close();
dirs.add(path.toString());
} else {
files.add(path.toString());
}
}
private static void z2zcopy(FileSystem src, FileSystem dst, String path,
int method)
throws IOException
{
Path srcPath = src.getPath(path);
Path dstPath = dst.getPath(path);
if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) {
if (!dstPath.exists()) {
try {
mkdirs(dstPath);
} catch (FileAlreadyExistsException x) {}
}
DirectoryStream<Path> ds = srcPath.newDirectoryStream();
for (Path child : ds) {
z2zcopy(src, dst,
path + (path.endsWith("/")?"":"/") + child.getName(),
method);
}
ds.close();
} else {
try {
if (dstPath.exists())
return;
switch (method) {
case 0:
srcPath.copyTo(dstPath);
break;
case 1:
chCopy(srcPath, dstPath);
break;
case 2:
//fchCopy(srcPath, dstPath);
streamCopy(srcPath, dstPath);
break;
}
} catch (FileAlreadyExistsException x) {}
}
}
private static void z2zmove(FileSystem src, FileSystem dst, String path)
throws IOException
{
Path srcPath = src.getPath(path);
Path dstPath = dst.getPath(path);
if (Boolean.TRUE.equals(srcPath.getAttribute("isDirectory"))) {
if (!dstPath.exists())
mkdirs(dstPath);
DirectoryStream<Path> ds = srcPath.newDirectoryStream();
for (Path child : ds) {
z2zmove(src, dst,
path + (path.endsWith("/")?"":"/") + child.getName());
}
ds.close();
} else {
//System.out.println("moving..." + path);
Path parent = dstPath.getParent();
if (parent != null && parent.notExists())
mkdirs(parent);
srcPath.moveTo(dstPath);
}
}
private static void walk(Path path) throws IOException
{
Files.walkFileTree(
path,
new SimpleFileVisitor<Path>() {
private int indent = 0;
private void indent() {
int n = 0;
while (n++ < indent)
System.out.printf(" ");
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs)
{
indent();
System.out.printf("%s%n", file.getName().toString());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs)
{
indent();
System.out.printf("[%s]%n", dir.toString());
indent += 2;
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir,
IOException ioe)
throws IOException
{
indent -= 2;
return FileVisitResult.CONTINUE;
}
});
}
private static void mkdirs(Path path) throws IOException {
path = path.toAbsolutePath();
Path parent = path.getParent();
if (parent != null) {
if (parent.notExists())
mkdirs(parent);
}
path.createDirectory();
}
private static void rmdirs(Path path) throws IOException {
while (path != null && path.getNameCount() != 0) {
path.delete();
path = path.getParent();
}
}
// check the content of two paths are equal
private static void checkEqual(Path src, Path dst) throws IOException
{
//System.out.printf("checking <%s> vs <%s>...%n",
// src.toString(), dst.toString());
//streams
InputStream isSrc = src.newInputStream();
InputStream isDst = dst.newInputStream();
byte[] bufSrc = new byte[8192];
byte[] bufDst = new byte[8192];
try {
int nSrc = 0;
while ((nSrc = isSrc.read(bufSrc)) != -1) {
int nDst = 0;
while (nDst < nSrc) {
int n = isDst.read(bufDst, nDst, nSrc - nDst);
if (n == -1) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
nDst += n;
}
while (--nSrc >= 0) {
if (bufSrc[nSrc] != bufDst[nSrc]) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
nSrc--;
}
}
} finally {
isSrc.close();
isDst.close();
}
// channels
SeekableByteChannel chSrc = src.newByteChannel();
SeekableByteChannel chDst = dst.newByteChannel();
if (chSrc.size() != chDst.size()) {
System.out.printf("src[%s].size=%d, dst[%s].size=%d%n",
chSrc.toString(), chSrc.size(),
chDst.toString(), chDst.size());
throw new RuntimeException("CHECK FAILED!");
}
ByteBuffer bbSrc = ByteBuffer.allocate(8192);
ByteBuffer bbDst = ByteBuffer.allocate(8192);
try {
int nSrc = 0;
while ((nSrc = chSrc.read(bbSrc)) != -1) {
int nDst = chDst.read(bbDst);
if (nSrc != nDst) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
while (--nSrc >= 0) {
if (bbSrc.get(nSrc) != bbDst.get(nSrc)) {
System.out.printf("checking <%s> vs <%s>...%n",
src.toString(), dst.toString());
throw new RuntimeException("CHECK FAILED!");
}
nSrc--;
}
bbSrc.flip();
bbDst.flip();
}
} catch (IOException x) {
x.printStackTrace();
} finally {
chSrc.close();
chDst.close();
}
}
private static void fchCopy(Path src, Path dst) throws IOException
{
Set<OpenOption> read = new HashSet<>();
read.add(READ);
Set<OpenOption> openwrite = new HashSet<>();
openwrite.add(CREATE_NEW);
openwrite.add(WRITE);
FileChannel srcFc = src.getFileSystem()
.provider()
.newFileChannel(src, read);
FileChannel dstFc = dst.getFileSystem()
.provider()
.newFileChannel(dst, openwrite);
try {
ByteBuffer bb = ByteBuffer.allocate(8192);
while (srcFc.read(bb) >= 0) {
bb.flip();
dstFc.write(bb);
bb.clear();
}
} finally {
srcFc.close();
dstFc.close();
}
}
private static void chCopy(Path src, Path dst) throws IOException
{
Set<OpenOption> read = new HashSet<>();
read.add(READ);
Set<OpenOption> openwrite = new HashSet<>();
openwrite.add(CREATE_NEW);
openwrite.add(WRITE);
SeekableByteChannel srcCh = src.newByteChannel(read);
SeekableByteChannel dstCh = dst.newByteChannel(openwrite);
try {
ByteBuffer bb = ByteBuffer.allocate(8192);
while (srcCh.read(bb) >= 0) {
bb.flip();
dstCh.write(bb);
bb.clear();
}
} finally {
srcCh.close();
dstCh.close();
}
}
private static void streamCopy(Path src, Path dst) throws IOException
{
InputStream isSrc = src.newInputStream();
OutputStream osDst = dst.newOutputStream();
byte[] buf = new byte[8192];
try {
int n = 0;
while ((n = isSrc.read(buf)) != -1) {
osDst.write(buf, 0, n);
}
} finally {
isSrc.close();
osDst.close();
}
}
static void channel(FileSystem fs, Path path)
throws Exception
{
System.out.println("test ByteChannel...");
SeekableByteChannel sbc = path.newByteChannel();
Set<OpenOption> read = new HashSet<>();
read.add(READ);
System.out.printf(" sbc[0]: pos=%d, size=%d%n", sbc.position(), sbc.size());
ByteBuffer bb = ByteBuffer.allocate((int)sbc.size());
int n = sbc.read(bb);
System.out.printf(" sbc[1]: read=%d, pos=%d, size=%d%n",
n, sbc.position(), sbc.size());
ByteBuffer bb2 = ByteBuffer.allocate((int)sbc.size());
int N = 120;
sbc.close();
// sbc.position(pos) is not supported in current version
// try the FileChannel
sbc = fs.provider().newFileChannel(path, read);
sbc.position(N);
System.out.printf(" sbc[2]: pos=%d, size=%d%n",
sbc.position(), sbc.size());
bb2.limit(100);
n = sbc.read(bb2);
System.out.printf(" sbc[3]: read=%d, pos=%d, size=%d%n",
n, sbc.position(), sbc.size());
System.out.printf(" sbc[4]: bb[%d]=%d, bb1[0]=%d%n",
N, bb.get(N) & 0xff, bb2.get(0) & 0xff);
sbc.close();
}
// create parents if does not exist
static Path getPathWithParents(FileSystem fs, String name)
throws Exception
{
Path path = fs.getPath(name);
Path parent = path.getParent();
if (parent != null && parent.notExists())
mkdirs(parent);
return path;
}
}
#
# Copyright (c) 2009, 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 6990846
# @summary Test ZipFileSystem demo
# @build Basic PathOps ZipFSTester
# @run shell basic.sh
if [ -z "${TESTJAVA}" ]; then
echo "Test must be run with jtreg"
exit 0
fi
ZIPFS="${TESTJAVA}/demo/nio/zipfs/zipfs.jar"
if [ ! -r "${ZIPFS}" ]; then
echo "${ZIPFS} not found"
exit 0
fi
OS=`uname -s`
case "$OS" in
Windows_* )
CLASSPATH="${TESTCLASSES};${ZIPFS}"
;;
* )
CLASSPATH="${TESTCLASSES}:${ZIPFS}"
;;
esac
export CLASSPATH
failures=0
go() {
echo ""
${TESTJAVA}/bin/java $1 $2 $3 2>&1
if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
}
# Run the tests
go Basic "${ZIPFS}"
go PathOps "${ZIPFS}"
go ZipFSTester "${ZIPFS}"
#
# Results
#
if [ $failures -gt 0 ];
then echo "$failures tests failed";
else echo "All tests passed";
fi
exit $failures
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册