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.attribute.PosixFileAttributes; 42 import java.nio.file.spi.FileSystemProvider; 43 import java.util.HashMap; 44 import java.util.Map; 45 import java.util.Set; 46 import java.util.concurrent.ExecutorService; 47 import java.util.zip.ZipException; 48 49 /** 50 * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal 51 */ 52 public class ZipFileSystemProvider extends FileSystemProvider { 53 54 private final Map<Path, ZipFileSystem> filesystems = new HashMap<>(); 55 56 public ZipFileSystemProvider() {} 57 58 @Override 59 public String getScheme() { 60 return "jar"; 61 } 62 63 protected Path uriToPath(URI uri) { 64 String scheme = uri.getScheme(); 65 if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { 66 throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); 67 } 68 try { 69 // only support legacy JAR URL syntax jar:{uri}!/{entry} for now 70 String spec = uri.getRawSchemeSpecificPart(); 71 int sep = spec.indexOf("!/"); 72 if (sep != -1) { 73 spec = spec.substring(0, sep); 74 } 75 return Paths.get(new URI(spec)).toAbsolutePath(); 76 } catch (URISyntaxException e) { 77 throw new IllegalArgumentException(e.getMessage(), e); 78 } 79 } 80 81 private boolean ensureFile(Path path) { 82 try { 83 BasicFileAttributes attrs = 84 Files.readAttributes(path, BasicFileAttributes.class); 85 if (!attrs.isRegularFile()) 86 throw new UnsupportedOperationException(); 87 return true; 88 } catch (IOException ioe) { 89 return false; 90 } 91 } 92 93 @Override 94 public FileSystem newFileSystem(URI uri, Map<String, ?> env) 95 throws IOException 96 { 97 Path path = uriToPath(uri); 98 synchronized(filesystems) { 99 Path realPath = null; 100 if (ensureFile(path)) { 101 realPath = path.toRealPath(); 102 if (filesystems.containsKey(realPath)) 103 throw new FileSystemAlreadyExistsException(); 104 } 105 ZipFileSystem zipfs = null; 106 try { 107 if (env.containsKey("multi-release")) { 108 zipfs = new JarFileSystem(this, path, env); 109 } else { 110 zipfs = new ZipFileSystem(this, path, env); 111 } 112 } catch (ZipException ze) { 113 String pname = path.toString(); 114 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 115 throw ze; 116 // assume NOT a zip/jar file 117 throw new UnsupportedOperationException(); 118 } 119 if (realPath == null) { // newly created 120 realPath = path.toRealPath(); 121 } 122 filesystems.put(realPath, zipfs); 123 return zipfs; 124 } 125 } 126 127 @Override 128 public FileSystem newFileSystem(Path path, Map<String, ?> env) 129 throws IOException 130 { 131 ensureFile(path); 132 try { 133 ZipFileSystem zipfs; 134 if (env.containsKey("multi-release")) { 135 zipfs = new JarFileSystem(this, path, env); 136 } else { 137 zipfs = new ZipFileSystem(this, path, env); 138 } 139 return zipfs; 140 } catch (ZipException ze) { 141 String pname = path.toString(); 142 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 143 throw ze; 144 throw new UnsupportedOperationException(); 145 } 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 static final 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 ZipFileAttributeView.get(toZipPath(path), 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 throws IOException 243 { 244 throw new UnsupportedOperationException(); 245 } 246 247 @Override 248 public SeekableByteChannel newByteChannel(Path path, 249 Set<? extends OpenOption> options, 250 FileAttribute<?>... attrs) 251 throws IOException 252 { 253 return toZipPath(path).newByteChannel(options, attrs); 254 } 255 256 @Override 257 public DirectoryStream<Path> newDirectoryStream( 258 Path path, Filter<? super Path> filter) throws IOException 259 { 260 return toZipPath(path).newDirectoryStream(filter); 261 } 262 263 @Override 264 public FileChannel newFileChannel(Path path, 265 Set<? extends OpenOption> options, 266 FileAttribute<?>... attrs) 267 throws IOException 268 { 269 return toZipPath(path).newFileChannel(options, attrs); 270 } 271 272 @Override 273 public InputStream newInputStream(Path path, OpenOption... options) 274 throws IOException 275 { 276 return toZipPath(path).newInputStream(options); 277 } 278 279 @Override 280 public OutputStream newOutputStream(Path path, OpenOption... options) 281 throws IOException 282 { 283 return toZipPath(path).newOutputStream(options); 284 } 285 286 @Override 287 @SuppressWarnings("unchecked") // Cast to A 288 public <A extends BasicFileAttributes> A 289 readAttributes(Path path, Class<A> type, LinkOption... options) 290 throws IOException 291 { 292 if (type == BasicFileAttributes.class || 293 type == PosixFileAttributes.class || 294 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 attributes, LinkOption... options) 302 throws IOException 303 { 304 return toZipPath(path).readAttributes(attributes, 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 zfpath = zfpath.toRealPath(); 324 if (filesystems.get(zfpath) == zfs) 325 filesystems.remove(zfpath); 326 } 327 } 328 }