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