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.security.AccessController; 43 import java.security.PrivilegedActionException; 44 import java.security.PrivilegedExceptionAction; 45 import java.util.HashMap; 46 import java.util.Map; 47 import java.util.Set; 48 import java.util.concurrent.ExecutorService; 49 import java.util.zip.ZipException; 50 51 /** 52 * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal 53 */ 54 public class ZipFileSystemProvider extends FileSystemProvider { 55 56 private final Map<Path, ZipFileSystem> filesystems = new HashMap<>(); 57 58 public ZipFileSystemProvider() {} 59 60 @Override 61 public String getScheme() { 62 return "jar"; 63 } 64 65 protected Path uriToPath(URI uri) { 66 String scheme = uri.getScheme(); 67 if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { 68 throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); 69 } 70 try { 71 // only support legacy JAR URL syntax jar:{uri}!/{entry} for now 72 String spec = uri.getRawSchemeSpecificPart(); 73 int sep = spec.indexOf("!/"); 74 if (sep != -1) { 75 spec = spec.substring(0, sep); 76 } 77 return Paths.get(new URI(spec)).toAbsolutePath(); 78 } catch (URISyntaxException e) { 79 throw new IllegalArgumentException(e.getMessage(), e); 80 } 81 } 82 83 private boolean ensureFile(Path path) { 84 try { 85 BasicFileAttributes attrs = 86 Files.readAttributes(path, BasicFileAttributes.class); 87 if (!attrs.isRegularFile()) 88 throw new UnsupportedOperationException(); 89 return true; 90 } catch (IOException ioe) { 91 return false; 92 } 93 } 94 95 @Override 96 public FileSystem newFileSystem(URI uri, Map<String, ?> env) 97 throws IOException 98 { 99 Path path = uriToPath(uri); 100 synchronized(filesystems) { 101 Path realPath = null; 102 if (ensureFile(path)) { 103 realPath = path.toRealPath(); 104 if (filesystems.containsKey(realPath)) 105 throw new FileSystemAlreadyExistsException(); 106 } 107 ZipFileSystem zipfs = null; 108 try { 109 if (env.containsKey("multi-release")) { 110 zipfs = new JarFileSystem(this, path, env); 111 } else { 112 zipfs = new ZipFileSystem(this, path, env); 113 } 114 } catch (ZipException ze) { 115 String pname = path.toString(); 116 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 117 throw ze; 118 // assume NOT a zip/jar file 119 throw new UnsupportedOperationException(); 120 } 121 if (realPath == null) { // newly created 122 realPath = path.toRealPath(); 123 } 124 filesystems.put(realPath, zipfs); 125 return zipfs; 126 } 127 } 128 129 @Override 130 public FileSystem newFileSystem(Path path, Map<String, ?> env) 131 throws IOException 132 { 133 ensureFile(path); 134 try { 135 ZipFileSystem zipfs; 136 if (env.containsKey("multi-release")) { 137 zipfs = new JarFileSystem(this, path, env); 138 } else { 139 zipfs = new ZipFileSystem(this, path, env); 140 } 141 return zipfs; 142 } catch (ZipException ze) { 143 String pname = path.toString(); 144 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 145 throw ze; 146 throw new UnsupportedOperationException(); 147 } 148 } 149 150 @Override 151 public Path getPath(URI uri) { 152 String spec = uri.getSchemeSpecificPart(); 153 int sep = spec.indexOf("!/"); 154 if (sep == -1) 155 throw new IllegalArgumentException("URI: " 156 + uri 157 + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR"); 158 return getFileSystem(uri).getPath(spec.substring(sep + 1)); 159 } 160 161 162 @Override 163 public FileSystem getFileSystem(URI uri) { 164 synchronized (filesystems) { 165 ZipFileSystem zipfs = null; 166 try { 167 zipfs = filesystems.get(uriToPath(uri).toRealPath()); 168 } catch (IOException x) { 169 // ignore the ioe from toRealPath(), return FSNFE 170 } 171 if (zipfs == null) 172 throw new FileSystemNotFoundException(); 173 return zipfs; 174 } 175 } 176 177 // Checks that the given file is a UnixPath 178 static final ZipPath toZipPath(Path path) { 179 if (path == null) 180 throw new NullPointerException(); 181 if (!(path instanceof ZipPath)) 182 throw new ProviderMismatchException(); 183 return (ZipPath)path; 184 } 185 186 @Override 187 public void checkAccess(Path path, AccessMode... modes) throws IOException { 188 toZipPath(path).checkAccess(modes); 189 } 190 191 @Override 192 public void copy(Path src, Path target, CopyOption... options) 193 throws IOException 194 { 195 toZipPath(src).copy(toZipPath(target), options); 196 } 197 198 @Override 199 public void createDirectory(Path path, FileAttribute<?>... attrs) 200 throws IOException 201 { 202 toZipPath(path).createDirectory(attrs); 203 } 204 205 @Override 206 public final void delete(Path path) throws IOException { 207 toZipPath(path).delete(); 208 } 209 210 @Override 211 public <V extends FileAttributeView> V 212 getFileAttributeView(Path path, Class<V> type, LinkOption... options) 213 { 214 return ZipFileAttributeView.get(toZipPath(path), type); 215 } 216 217 @Override 218 public FileStore getFileStore(Path path) throws IOException { 219 return toZipPath(path).getFileStore(); 220 } 221 222 @Override 223 public boolean isHidden(Path path) { 224 return toZipPath(path).isHidden(); 225 } 226 227 @Override 228 public boolean isSameFile(Path path, Path other) throws IOException { 229 return toZipPath(path).isSameFile(other); 230 } 231 232 @Override 233 public void move(Path src, Path target, CopyOption... options) 234 throws IOException 235 { 236 toZipPath(src).move(toZipPath(target), options); 237 } 238 239 @Override 240 public AsynchronousFileChannel newAsynchronousFileChannel(Path path, 241 Set<? extends OpenOption> options, 242 ExecutorService exec, 243 FileAttribute<?>... attrs) 244 throws IOException 245 { 246 throw new UnsupportedOperationException(); 247 } 248 249 @Override 250 public SeekableByteChannel newByteChannel(Path path, 251 Set<? extends OpenOption> options, 252 FileAttribute<?>... attrs) 253 throws IOException 254 { 255 return toZipPath(path).newByteChannel(options, attrs); 256 } 257 258 @Override 259 public DirectoryStream<Path> newDirectoryStream( 260 Path path, Filter<? super Path> filter) throws IOException 261 { 262 return toZipPath(path).newDirectoryStream(filter); 263 } 264 265 @Override 266 public FileChannel newFileChannel(Path path, 267 Set<? extends OpenOption> options, 268 FileAttribute<?>... attrs) 269 throws IOException 270 { 271 return toZipPath(path).newFileChannel(options, attrs); 272 } 273 274 @Override 275 public InputStream newInputStream(Path path, OpenOption... options) 276 throws IOException 277 { 278 return toZipPath(path).newInputStream(options); 279 } 280 281 @Override 282 public OutputStream newOutputStream(Path path, OpenOption... options) 283 throws IOException 284 { 285 return toZipPath(path).newOutputStream(options); 286 } 287 288 @Override 289 @SuppressWarnings("unchecked") // Cast to A 290 public <A extends BasicFileAttributes> A 291 readAttributes(Path path, Class<A> type, LinkOption... options) 292 throws IOException 293 { 294 if (type == BasicFileAttributes.class || type == ZipFileAttributes.class) 295 return (A)toZipPath(path).getAttributes(); 296 return null; 297 } 298 299 @Override 300 public Map<String, Object> 301 readAttributes(Path path, String attribute, LinkOption... options) 302 throws IOException 303 { 304 return toZipPath(path).readAttributes(attribute, options); 305 } 306 307 @Override 308 public Path readSymbolicLink(Path link) throws IOException { 309 throw new UnsupportedOperationException("Not supported."); 310 } 311 312 @Override 313 public void setAttribute(Path path, String attribute, 314 Object value, LinkOption... options) 315 throws IOException 316 { 317 toZipPath(path).setAttribute(attribute, value, options); 318 } 319 320 ////////////////////////////////////////////////////////////// 321 void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { 322 synchronized (filesystems) { 323 Path tempPath = zfpath; 324 PrivilegedExceptionAction<Path> action = tempPath::toRealPath; 325 try { 326 zfpath = AccessController.doPrivileged(action); 327 } catch (PrivilegedActionException e) { 328 throw (IOException) e.getException(); 329 } 330 if (filesystems.get(zfpath) == zfs) 331 filesystems.remove(zfpath); 332 } 333 } 334 }