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