1 /* 2 * Copyright (c) 2009, 2014, 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.*; 29 import java.nio.channels.*; 30 import java.nio.file.*; 31 import java.nio.file.DirectoryStream.Filter; 32 import java.nio.file.attribute.*; 33 import java.nio.file.spi.FileSystemProvider; 34 import java.net.URI; 35 import java.net.URISyntaxException; 36 import java.util.HashMap; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.zip.ZipException; 40 import java.util.concurrent.ExecutorService; 41 42 /* 43 * 44 * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal 45 */ 46 47 public class ZipFileSystemProvider extends FileSystemProvider { 48 49 50 private final Map<Path, ZipFileSystem> filesystems = new HashMap<>(); 51 52 public ZipFileSystemProvider() {} 53 54 @Override 55 public String getScheme() { 56 return "jar"; 57 } 58 59 protected Path uriToPath(URI uri) { 60 String scheme = uri.getScheme(); 61 if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { 62 throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); 63 } 64 try { 65 // only support legacy JAR URL syntax jar:{uri}!/{entry} for now 66 String spec = uri.getRawSchemeSpecificPart(); 67 int sep = spec.indexOf("!/"); 68 if (sep != -1) { 69 spec = spec.substring(0, sep); 70 } 71 return Paths.get(new URI(spec)).toAbsolutePath(); 72 } catch (URISyntaxException e) { 73 throw new IllegalArgumentException(e.getMessage(), e); 74 } 75 } 76 77 private boolean ensureFile(Path path) { 78 try { 79 BasicFileAttributes attrs = 80 Files.readAttributes(path, BasicFileAttributes.class); 81 if (!attrs.isRegularFile()) 82 throw new UnsupportedOperationException(); 83 return true; 84 } catch (IOException ioe) { 85 return false; 86 } 87 } 88 89 @Override 90 public FileSystem newFileSystem(URI uri, Map<String, ?> env) 91 throws IOException 92 { 93 Path path = uriToPath(uri); 94 synchronized(filesystems) { 95 Path realPath = null; 96 if (ensureFile(path)) { 97 realPath = path.toRealPath(); 98 if (filesystems.containsKey(realPath)) 99 throw new FileSystemAlreadyExistsException(); 100 } 101 ZipFileSystem zipfs = null; 102 try { 103 if (env.containsKey("multi-release")) { 104 zipfs = new JarFileSystem(this, path, env); 105 } else { 106 zipfs = new ZipFileSystem(this, path, env); 107 } 108 } catch (ZipException ze) { 109 String pname = path.toString(); 110 if (pname.endsWith(".zip") || pname.endsWith(".jar")) 111 throw ze; 112 // assume NOT a zip/jar file 113 throw new UnsupportedOperationException(); 114 } 115 if (realPath == null) { // newly created 116 realPath = path.toRealPath(); 117 } 118 filesystems.put(realPath, zipfs); 119 return zipfs; 120 } 121 } 122 123 @Override 124 public FileSystem newFileSystem(Path path, Map<String, ?> env) 125 throws IOException 126 { 127 if (path.getFileSystem() != FileSystems.getDefault()) { 128 throw new UnsupportedOperationException(); 129 } 130 ensureFile(path); 131 try { 132 ZipFileSystem zipfs; 133 if (env.containsKey("multi-release")) { 134 zipfs = new JarFileSystem(this, path, env); 135 } else { 136 zipfs = new ZipFileSystem(this, path, env); 137 } 138 return zipfs; 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 } 146 147 @Override 148 public Path getPath(URI uri) { 149 String spec = uri.getSchemeSpecificPart(); 150 int sep = spec.indexOf("!/"); 151 if (sep == -1) 152 throw new IllegalArgumentException("URI: " 153 + uri 154 + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR"); 155 return getFileSystem(uri).getPath(spec.substring(sep + 1)); 156 } 157 158 159 @Override 160 public FileSystem getFileSystem(URI uri) { 161 synchronized (filesystems) { 162 ZipFileSystem zipfs = null; 163 try { 164 zipfs = filesystems.get(uriToPath(uri).toRealPath()); 165 } catch (IOException x) { 166 // ignore the ioe from toRealPath(), return FSNFE 167 } 168 if (zipfs == null) 169 throw new FileSystemNotFoundException(); 170 return zipfs; 171 } 172 } 173 174 // Checks that the given file is a UnixPath 175 static final ZipPath toZipPath(Path path) { 176 if (path == null) 177 throw new NullPointerException(); 178 if (!(path instanceof ZipPath)) 179 throw new ProviderMismatchException(); 180 return (ZipPath)path; 181 } 182 183 @Override 184 public void checkAccess(Path path, AccessMode... modes) throws IOException { 185 toZipPath(path).checkAccess(modes); 186 } 187 188 @Override 189 public void copy(Path src, Path target, CopyOption... options) 190 throws IOException 191 { 192 toZipPath(src).copy(toZipPath(target), options); 193 } 194 195 @Override 196 public void createDirectory(Path path, FileAttribute<?>... attrs) 197 throws IOException 198 { 199 toZipPath(path).createDirectory(attrs); 200 } 201 202 @Override 203 public final void delete(Path path) throws IOException { 204 toZipPath(path).delete(); 205 } 206 207 @Override 208 @SuppressWarnings("unchecked") 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 || type == ZipFileAttributes.class) 293 return (A)toZipPath(path).getAttributes(); 294 return null; 295 } 296 297 @Override 298 public Map<String, Object> 299 readAttributes(Path path, String attribute, LinkOption... options) 300 throws IOException 301 { 302 return toZipPath(path).readAttributes(attribute, options); 303 } 304 305 @Override 306 public Path readSymbolicLink(Path link) throws IOException { 307 throw new UnsupportedOperationException("Not supported."); 308 } 309 310 @Override 311 public void setAttribute(Path path, String attribute, 312 Object value, LinkOption... options) 313 throws IOException 314 { 315 toZipPath(path).setAttribute(attribute, value, options); 316 } 317 318 ////////////////////////////////////////////////////////////// 319 void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { 320 synchronized (filesystems) { 321 zfpath = zfpath.toRealPath(); 322 if (filesystems.get(zfpath) == zfs) 323 filesystems.remove(zfpath); 324 } 325 } 326 }