1 /* 2 * Copyright (c) 2009, 2019, 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 // Property used to specify the entry version to use for a multi-release JAR 57 static final String PROPERTY_RELEASE_VERSION = "releaseVersion"; 58 // Original property used to specify the entry version to use for a 59 // multi-release JAR which is kept for backwards compatibility. 60 static final String PROPERTY_MULTI_RELEASE = "multi-release"; 61 private final Map<Path, ZipFileSystem> filesystems = new HashMap<>(); 62 63 public ZipFileSystemProvider() {} 64 65 @Override 66 public String getScheme() { 67 return "jar"; 68 } 69 70 protected Path uriToPath(URI uri) { 71 String scheme = uri.getScheme(); 72 if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { 73 throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); 74 } 75 try { 76 // only support legacy JAR URL syntax jar:{uri}!/{entry} for now 77 String spec = uri.getRawSchemeSpecificPart(); 78 int sep = spec.indexOf("!/"); 79 if (sep != -1) { 80 spec = spec.substring(0, sep); 81 } 82 return Paths.get(new URI(spec)).toAbsolutePath(); 83 } catch (URISyntaxException e) { 84 throw new IllegalArgumentException(e.getMessage(), e); 85 } 86 } 87 88 private boolean ensureFile(Path path) { 89 try { 90 BasicFileAttributes attrs = 91 Files.readAttributes(path, BasicFileAttributes.class); 92 if (!attrs.isRegularFile()) 93 throw new UnsupportedOperationException(); 94 return true; 95 } catch (IOException ioe) { 96 return false; 97 } 98 } 99 100 @Override 101 public FileSystem newFileSystem(URI uri, Map<String, ?> env) 102 throws IOException 103 { 104 Path path = uriToPath(uri); 105 synchronized(filesystems) { 106 Path realPath = null; 107 if (ensureFile(path)) { 108 realPath = path.toRealPath(); 109 if (filesystems.containsKey(realPath)) 110 throw new FileSystemAlreadyExistsException(); 111 } 112 ZipFileSystem zipfs = getZipFileSystem(path, env); 113 if (realPath == null) { // newly created 114 realPath = path.toRealPath(); 115 } 116 filesystems.put(realPath, zipfs); 117 return zipfs; 118 } 119 } 120 121 @Override 122 public FileSystem newFileSystem(Path path, Map<String, ?> env) 123 throws IOException 124 { 125 ensureFile(path); 126 return getZipFileSystem(path, env); 127 } 128 129 private ZipFileSystem getZipFileSystem(Path path, Map<String, ?> env) throws IOException { 130 ZipFileSystem zipfs; 131 try { 132 if (env.containsKey(PROPERTY_RELEASE_VERSION) || 133 env.containsKey(PROPERTY_MULTI_RELEASE)) { 134 zipfs = new JarFileSystem(this, path, env); 135 } else { 136 zipfs = new ZipFileSystem(this, path, env); 137 } 138 } catch (ZipException ze) { 139 String pname = path.toString(); 140 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 141 throw ze; 142 throw new UnsupportedOperationException(); 143 } 144 return zipfs; 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 private static 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 toZipPath(path).getFileAttributeView(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 { 242 throw new UnsupportedOperationException(); 243 } 244 245 @Override 246 public SeekableByteChannel newByteChannel(Path path, 247 Set<? extends OpenOption> options, 248 FileAttribute<?>... attrs) 249 throws IOException 250 { 251 return toZipPath(path).newByteChannel(options, attrs); 252 } 253 254 @Override 255 public DirectoryStream<Path> newDirectoryStream( 256 Path path, Filter<? super Path> filter) throws IOException 257 { 258 return toZipPath(path).newDirectoryStream(filter); 259 } 260 261 @Override 262 public FileChannel newFileChannel(Path path, 263 Set<? extends OpenOption> options, 264 FileAttribute<?>... attrs) 265 throws IOException 266 { 267 return toZipPath(path).newFileChannel(options, attrs); 268 } 269 270 @Override 271 public InputStream newInputStream(Path path, OpenOption... options) 272 throws IOException 273 { 274 return toZipPath(path).newInputStream(options); 275 } 276 277 @Override 278 public OutputStream newOutputStream(Path path, OpenOption... options) 279 throws IOException 280 { 281 return toZipPath(path).newOutputStream(options); 282 } 283 284 @Override 285 public <A extends BasicFileAttributes> A 286 readAttributes(Path path, Class<A> type, LinkOption... options) 287 throws IOException 288 { 289 return toZipPath(path).readAttributes(type); 290 } 291 292 @Override 293 public Map<String, Object> 294 readAttributes(Path path, String attributes, LinkOption... options) 295 throws IOException 296 { 297 return toZipPath(path).readAttributes(attributes, options); 298 } 299 300 @Override 301 public Path readSymbolicLink(Path link) { 302 throw new UnsupportedOperationException("Not supported."); 303 } 304 305 @Override 306 public void setAttribute(Path path, String attribute, 307 Object value, LinkOption... options) 308 throws IOException 309 { 310 toZipPath(path).setAttribute(attribute, value, options); 311 } 312 313 ////////////////////////////////////////////////////////////// 314 void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { 315 synchronized (filesystems) { 316 Path tempPath = zfpath; 317 PrivilegedExceptionAction<Path> action = tempPath::toRealPath; 318 try { 319 zfpath = AccessController.doPrivileged(action); 320 } catch (PrivilegedActionException e) { 321 throw (IOException) e.getException(); 322 } 323 if (filesystems.get(zfpath) == zfs) 324 filesystems.remove(zfpath); 325 } 326 } 327 }