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 protected static final String 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 compatability. 60 protected static final String 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 ZipFileSystem zipfs = getZipFileSystem(path, env); 127 return zipfs; 128 } 129 130 private ZipFileSystem getZipFileSystem(Path path, Map<String, ?> env) throws IOException { 131 ZipFileSystem zipfs; 132 try { 133 if (env.containsKey(RELEASE_VERSION) || 134 env.containsKey(MULTI_RELEASE)) { 135 zipfs = new JarFileSystem(this, path, env); 136 } else { 137 zipfs = new ZipFileSystem(this, path, env); 138 } 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 return zipfs; 146 } 147 148 @Override 149 public Path getPath(URI uri) { 150 String spec = uri.getSchemeSpecificPart(); 151 int sep = spec.indexOf("!/"); 152 if (sep == -1) 153 throw new IllegalArgumentException("URI: " 154 + uri 155 + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR"); 156 return getFileSystem(uri).getPath(spec.substring(sep + 1)); 157 } 158 159 160 @Override 161 public FileSystem getFileSystem(URI uri) { 162 synchronized (filesystems) { 163 ZipFileSystem zipfs = null; 164 try { 165 zipfs = filesystems.get(uriToPath(uri).toRealPath()); 166 } catch (IOException x) { 167 // ignore the ioe from toRealPath(), return FSNFE 168 } 169 if (zipfs == null) 170 throw new FileSystemNotFoundException(); 171 return zipfs; 172 } 173 } 174 175 // Checks that the given file is a UnixPath 176 private static ZipPath toZipPath(Path path) { 177 if (path == null) 178 throw new NullPointerException(); 179 if (!(path instanceof ZipPath)) 180 throw new ProviderMismatchException(); 181 return (ZipPath)path; 182 } 183 184 @Override 185 public void checkAccess(Path path, AccessMode... modes) throws IOException { 186 toZipPath(path).checkAccess(modes); 187 } 188 189 @Override 190 public void copy(Path src, Path target, CopyOption... options) 191 throws IOException 192 { 193 toZipPath(src).copy(toZipPath(target), options); 194 } 195 196 @Override 197 public void createDirectory(Path path, FileAttribute<?>... attrs) 198 throws IOException 199 { 200 toZipPath(path).createDirectory(attrs); 201 } 202 203 @Override 204 public final void delete(Path path) throws IOException { 205 toZipPath(path).delete(); 206 } 207 208 @Override 209 public <V extends FileAttributeView> V 210 getFileAttributeView(Path path, Class<V> type, LinkOption... options) 211 { 212 return toZipPath(path).getFileAttributeView(type); 213 } 214 215 @Override 216 public FileStore getFileStore(Path path) throws IOException { 217 return toZipPath(path).getFileStore(); 218 } 219 220 @Override 221 public boolean isHidden(Path path) { 222 return toZipPath(path).isHidden(); 223 } 224 225 @Override 226 public boolean isSameFile(Path path, Path other) throws IOException { 227 return toZipPath(path).isSameFile(other); 228 } 229 230 @Override 231 public void move(Path src, Path target, CopyOption... options) 232 throws IOException 233 { 234 toZipPath(src).move(toZipPath(target), options); 235 } 236 237 @Override 238 public AsynchronousFileChannel newAsynchronousFileChannel(Path path, 239 Set<? extends OpenOption> options, 240 ExecutorService exec, 241 FileAttribute<?>... attrs) 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 public <A extends BasicFileAttributes> A 287 readAttributes(Path path, Class<A> type, LinkOption... options) 288 throws IOException 289 { 290 return toZipPath(path).readAttributes(type); 291 } 292 293 @Override 294 public Map<String, Object> 295 readAttributes(Path path, String attributes, LinkOption... options) 296 throws IOException 297 { 298 return toZipPath(path).readAttributes(attributes, options); 299 } 300 301 @Override 302 public Path readSymbolicLink(Path link) { 303 throw new UnsupportedOperationException("Not supported."); 304 } 305 306 @Override 307 public void setAttribute(Path path, String attribute, 308 Object value, LinkOption... options) 309 throws IOException 310 { 311 toZipPath(path).setAttribute(attribute, value, options); 312 } 313 314 ////////////////////////////////////////////////////////////// 315 void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { 316 synchronized (filesystems) { 317 Path tempPath = zfpath; 318 PrivilegedExceptionAction<Path> action = tempPath::toRealPath; 319 try { 320 zfpath = AccessController.doPrivileged(action); 321 } catch (PrivilegedActionException e) { 322 throw (IOException) e.getException(); 323 } 324 if (filesystems.get(zfpath) == zfs) 325 filesystems.remove(zfpath); 326 } 327 } 328 }