1 /* 2 * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nio.zipfs; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.OutputStream; 31 import java.net.URI; 32 import java.net.URISyntaxException; 33 import java.nio.channels.AsynchronousFileChannel; 34 import java.nio.channels.FileChannel; 35 import java.nio.channels.SeekableByteChannel; 36 import java.nio.file.*; 37 import java.nio.file.DirectoryStream.Filter; 38 import java.nio.file.attribute.BasicFileAttributes; 39 import java.nio.file.attribute.FileAttribute; 40 import java.nio.file.attribute.FileAttributeView; 41 import java.nio.file.spi.FileSystemProvider; 42 import java.util.HashMap; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.concurrent.ExecutorService; 46 import java.util.zip.ZipException; 47 48 /** 49 * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal 50 */ 51 public class ZipFileSystemProvider extends FileSystemProvider { 52 53 private final Map<Path, ZipFileSystem> filesystems = new HashMap<>(); 54 55 public ZipFileSystemProvider() {} 56 57 @Override 58 public String getScheme() { 59 return "jar"; 60 } 61 62 protected Path uriToPath(URI uri) { 63 String scheme = uri.getScheme(); 64 if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { 65 throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); 66 } 67 try { 68 // only support legacy JAR URL syntax jar:{uri}!/{entry} for now 69 String spec = uri.getRawSchemeSpecificPart(); 70 int sep = spec.indexOf("!/"); 71 if (sep != -1) { 72 spec = spec.substring(0, sep); 73 } 74 return Paths.get(new URI(spec)).toAbsolutePath(); 75 } catch (URISyntaxException e) { 76 throw new IllegalArgumentException(e.getMessage(), e); 77 } 78 } 79 80 private boolean ensureFile(Path path) { 81 try { 82 BasicFileAttributes attrs = 83 Files.readAttributes(path, BasicFileAttributes.class); 84 if (!attrs.isRegularFile()) 85 throw new UnsupportedOperationException(); 86 return true; 87 } catch (IOException ioe) { 88 return false; 89 } 90 } 91 92 @Override 93 public FileSystem newFileSystem(URI uri, Map<String, ?> env) 94 throws IOException 95 { 96 Path path = uriToPath(uri); 97 synchronized(filesystems) { 98 Path realPath = null; 99 if (ensureFile(path)) { 100 realPath = path.toRealPath(); 101 if (filesystems.containsKey(realPath)) 102 throw new FileSystemAlreadyExistsException(); 103 } 104 ZipFileSystem zipfs = null; 105 try { 106 if (env.containsKey("multi-release")) { 107 zipfs = new JarFileSystem(this, path, env); 108 } else { 109 zipfs = new ZipFileSystem(this, path, env); 110 } 111 } catch (ZipException ze) { 112 String pname = path.toString(); 113 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 114 throw ze; 115 // assume NOT a zip/jar file 116 throw new UnsupportedOperationException(); 117 } 118 if (realPath == null) { // newly created 119 realPath = path.toRealPath(); 120 } 121 filesystems.put(realPath, zipfs); 122 return zipfs; 123 } 124 } 125 126 @Override 127 public FileSystem newFileSystem(Path path, Map<String, ?> env) 128 throws IOException 129 { 130 ensureFile(path); 131 try { 132 ZipFileSystem zipfs; 133 if (env.containsKey("multi-release")) { 134 zipfs = new JarFileSystem(this, path, env); 135 } else { 136 zipfs = new ZipFileSystem(this, path, env); 137 } 138 return zipfs; 139 } catch (ZipException ze) { 140 String pname = path.toString(); 141 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 142 throw ze; 143 throw new UnsupportedOperationException(); 144 } 145 } 146 147 @Override 148 public Path getPath(URI uri) { 149 String spec = uri.getSchemeSpecificPart(); 150 int sep = spec.indexOf("!/"); 151 if (sep == -1) 152 throw new IllegalArgumentException("URI: " 153 + uri 154 + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR"); 155 return getFileSystem(uri).getPath(spec.substring(sep + 1)); 156 } 157 158 159 @Override 160 public FileSystem getFileSystem(URI uri) { 161 synchronized (filesystems) { 162 ZipFileSystem zipfs = null; 163 try { 164 zipfs = filesystems.get(uriToPath(uri).toRealPath()); 165 } catch (IOException x) { 166 // ignore the ioe from toRealPath(), return FSNFE 167 } 168 if (zipfs == null) 169 throw new FileSystemNotFoundException(); 170 return zipfs; 171 } 172 } 173 174 // Checks that the given file is a UnixPath 175 static final ZipPath toZipPath(Path path) { 176 if (path == null) 177 throw new NullPointerException(); 178 if (!(path instanceof ZipPath)) 179 throw new ProviderMismatchException(); 180 return (ZipPath)path; 181 } 182 183 @Override 184 public void checkAccess(Path path, AccessMode... modes) throws IOException { 185 toZipPath(path).checkAccess(modes); 186 } 187 188 @Override 189 public void copy(Path src, Path target, CopyOption... options) 190 throws IOException 191 { 192 toZipPath(src).copy(toZipPath(target), options); 193 } 194 195 @Override 196 public void createDirectory(Path path, FileAttribute<?>... attrs) 197 throws IOException 198 { 199 toZipPath(path).createDirectory(attrs); 200 } 201 202 @Override 203 public final void delete(Path path) throws IOException { 204 toZipPath(path).delete(); 205 } 206 207 @Override 208 public <V extends FileAttributeView> V 209 getFileAttributeView(Path path, Class<V> type, LinkOption... options) 210 { 211 return ZipFileAttributeView.get(toZipPath(path), type); 212 } 213 214 @Override 215 public FileStore getFileStore(Path path) throws IOException { 216 return toZipPath(path).getFileStore(); 217 } 218 219 @Override 220 public boolean isHidden(Path path) { 221 return toZipPath(path).isHidden(); 222 } 223 224 @Override 225 public boolean isSameFile(Path path, Path other) throws IOException { 226 return toZipPath(path).isSameFile(other); 227 } 228 229 @Override 230 public void move(Path src, Path target, CopyOption... options) 231 throws IOException 232 { 233 toZipPath(src).move(toZipPath(target), options); 234 } 235 236 @Override 237 public AsynchronousFileChannel newAsynchronousFileChannel(Path path, 238 Set<? extends OpenOption> options, 239 ExecutorService exec, 240 FileAttribute<?>... attrs) 241 throws IOException 242 { 243 throw new UnsupportedOperationException(); 244 } 245 246 @Override 247 public SeekableByteChannel newByteChannel(Path path, 248 Set<? extends OpenOption> options, 249 FileAttribute<?>... attrs) 250 throws IOException 251 { 252 return toZipPath(path).newByteChannel(options, attrs); 253 } 254 255 @Override 256 public DirectoryStream<Path> newDirectoryStream( 257 Path path, Filter<? super Path> filter) throws IOException 258 { 259 return toZipPath(path).newDirectoryStream(filter); 260 } 261 262 @Override 263 public FileChannel newFileChannel(Path path, 264 Set<? extends OpenOption> options, 265 FileAttribute<?>... attrs) 266 throws IOException 267 { 268 return toZipPath(path).newFileChannel(options, attrs); 269 } 270 271 @Override 272 public InputStream newInputStream(Path path, OpenOption... options) 273 throws IOException 274 { 275 return toZipPath(path).newInputStream(options); 276 } 277 278 @Override 279 public OutputStream newOutputStream(Path path, OpenOption... options) 280 throws IOException 281 { 282 return toZipPath(path).newOutputStream(options); 283 } 284 285 @Override 286 @SuppressWarnings("unchecked") // Cast to A 287 public <A extends BasicFileAttributes> A 288 readAttributes(Path path, Class<A> type, LinkOption... options) 289 throws IOException 290 { 291 if (type == BasicFileAttributes.class || type == ZipFileAttributes.class) 292 return (A)toZipPath(path).getAttributes(); 293 return null; 294 } 295 296 @Override 297 public Map<String, Object> 298 readAttributes(Path path, String attribute, LinkOption... options) 299 throws IOException 300 { 301 return toZipPath(path).readAttributes(attribute, options); 302 } 303 304 @Override 305 public Path readSymbolicLink(Path link) throws IOException { 306 throw new UnsupportedOperationException("Not supported."); 307 } 308 309 @Override 310 public void setAttribute(Path path, String attribute, 311 Object value, LinkOption... options) 312 throws IOException 313 { 314 toZipPath(path).setAttribute(attribute, value, options); 315 } 316 317 ////////////////////////////////////////////////////////////// 318 void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { 319 synchronized (filesystems) { 320 zfpath = zfpath.toRealPath(); 321 if (filesystems.get(zfpath) == zfs) 322 filesystems.remove(zfpath); 323 } 324 } 325 }