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