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