1 /*
   2  * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  *
   8  *   - Redistributions of source code must retain the above copyright
   9  *     notice, this list of conditions and the following disclaimer.
  10  *
  11  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 /*
  33  * This source code is provided to illustrate the usage of a given feature
  34  * or technique and has been deliberately simplified. Additional steps
  35  * required for a production-quality application, such as security checks,
  36  * input validation and proper error handling, might not be present in
  37  * this sample code.
  38  */
  39 
  40 
  41 package com.sun.nio.zipfs;
  42 
  43 import java.io.*;
  44 import java.nio.channels.*;
  45 import java.nio.file.*;
  46 import java.nio.file.DirectoryStream.Filter;
  47 import java.nio.file.attribute.*;
  48 import java.nio.file.spi.FileSystemProvider;
  49 import java.net.URI;
  50 import java.net.URISyntaxException;
  51 import java.util.HashMap;
  52 import java.util.Map;
  53 import java.util.Set;
  54 import java.util.zip.ZipError;
  55 import java.util.concurrent.ExecutorService;
  56 
  57 /*
  58  *
  59  * @author  Xueming Shen, Rajendra Gutupalli, Jaya Hangal
  60  */
  61 
  62 public class ZipFileSystemProvider extends FileSystemProvider {
  63 
  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             return Paths.get(new URI(spec)).toAbsolutePath();
  86         } catch (URISyntaxException e) {
  87             throw new IllegalArgumentException(e.getMessage(), e);
  88         }
  89     }
  90 
  91     private boolean ensureFile(Path path) {
  92         try {
  93             BasicFileAttributes attrs =
  94                 Files.readAttributes(path, BasicFileAttributes.class);
  95             if (!attrs.isRegularFile())
  96                 throw new UnsupportedOperationException();
  97             return true;
  98         } catch (IOException ioe) {
  99             return false;
 100         }
 101     }
 102 
 103     @Override
 104     public FileSystem newFileSystem(URI uri, Map<String, ?> env)
 105         throws IOException
 106     {
 107         Path path = uriToPath(uri);
 108         synchronized(filesystems) {
 109             Path realPath = null;
 110             if (ensureFile(path)) {
 111                 realPath = path.toRealPath();
 112                 if (filesystems.containsKey(realPath))
 113                     throw new FileSystemAlreadyExistsException();
 114             }
 115             ZipFileSystem zipfs = null;
 116             try {
 117                 zipfs = new ZipFileSystem(this, path, env);
 118             } catch (ZipError ze) {
 119                 String pname = path.toString();
 120                 if (pname.endsWith(".zip") || pname.endsWith(".jar"))
 121                     throw ze;
 122                 // assume NOT a zip/jar file
 123                 throw new UnsupportedOperationException();
 124             }
 125             filesystems.put(realPath, zipfs);
 126             return zipfs;
 127         }
 128     }
 129 
 130     @Override
 131     public FileSystem newFileSystem(Path path, Map<String, ?> env)
 132         throws IOException
 133     {
 134         if (path.getFileSystem() != FileSystems.getDefault()) {
 135             throw new UnsupportedOperationException();
 136         }
 137         ensureFile(path);
 138         try {
 139             return new ZipFileSystem(this, path, env);
 140         } catch (ZipError 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 
 151         String spec = uri.getSchemeSpecificPart();
 152         int sep = spec.indexOf("!/");
 153         if (sep == -1)
 154             throw new IllegalArgumentException("URI: "
 155                 + uri
 156                 + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR");
 157         return getFileSystem(uri).getPath(spec.substring(sep + 1));
 158     }
 159 
 160 
 161     @Override
 162     public FileSystem getFileSystem(URI uri) {
 163         synchronized (filesystems) {
 164             ZipFileSystem zipfs = null;
 165             try {
 166                 zipfs = filesystems.get(uriToPath(uri).toRealPath());
 167             } catch (IOException x) {
 168                 // ignore the ioe from toRealPath(), return FSNFE
 169             }
 170             if (zipfs == null)
 171                 throw new FileSystemNotFoundException();
 172             return zipfs;
 173         }
 174     }
 175 
 176     // Checks that the given file is a UnixPath
 177     static final ZipPath toZipPath(Path path) {
 178         if (path == null)
 179             throw new NullPointerException();
 180         if (!(path instanceof ZipPath))
 181             throw new ProviderMismatchException();
 182         return (ZipPath)path;
 183     }
 184 
 185     @Override
 186     public void checkAccess(Path path, AccessMode... modes) throws IOException {
 187         toZipPath(path).checkAccess(modes);
 188     }
 189 
 190     @Override
 191     public void copy(Path src, Path target, CopyOption... options)
 192         throws IOException
 193     {
 194         toZipPath(src).copy(toZipPath(target), options);
 195     }
 196 
 197     @Override
 198     public void createDirectory(Path path, FileAttribute<?>... attrs)
 199         throws IOException
 200     {
 201         toZipPath(path).createDirectory(attrs);
 202     }
 203 
 204     @Override
 205     public final void delete(Path path) throws IOException {
 206         toZipPath(path).delete();
 207     }
 208 
 209     @Override
 210     @SuppressWarnings("unchecked")
 211     public <V extends FileAttributeView> V
 212         getFileAttributeView(Path path, Class<V> type, LinkOption... options)
 213     {
 214         return ZipFileAttributeView.get(toZipPath(path), type);
 215     }
 216 
 217     @Override
 218     public FileStore getFileStore(Path path) throws IOException {
 219         return toZipPath(path).getFileStore();
 220     }
 221 
 222     @Override
 223     public boolean isHidden(Path path) {
 224         return toZipPath(path).isHidden();
 225     }
 226 
 227     @Override
 228     public boolean isSameFile(Path path, Path other) throws IOException {
 229         return toZipPath(path).isSameFile(other);
 230     }
 231 
 232     @Override
 233     public void move(Path src, Path target, CopyOption... options)
 234         throws IOException
 235     {
 236         toZipPath(src).move(toZipPath(target), options);
 237     }
 238 
 239     @Override
 240     public AsynchronousFileChannel newAsynchronousFileChannel(Path path,
 241             Set<? extends OpenOption> options,
 242             ExecutorService exec,
 243             FileAttribute<?>... attrs)
 244             throws IOException
 245     {
 246         throw new UnsupportedOperationException();
 247     }
 248 
 249     @Override
 250     public SeekableByteChannel newByteChannel(Path path,
 251                                               Set<? extends OpenOption> options,
 252                                               FileAttribute<?>... attrs)
 253         throws IOException
 254     {
 255         return toZipPath(path).newByteChannel(options, attrs);
 256     }
 257 
 258     @Override
 259     public DirectoryStream<Path> newDirectoryStream(
 260         Path path, Filter<? super Path> filter) throws IOException
 261     {
 262         return toZipPath(path).newDirectoryStream(filter);
 263     }
 264 
 265     @Override
 266     public FileChannel newFileChannel(Path path,
 267                                       Set<? extends OpenOption> options,
 268                                       FileAttribute<?>... attrs)
 269         throws IOException
 270     {
 271         return toZipPath(path).newFileChannel(options, attrs);
 272     }
 273 
 274     @Override
 275     public InputStream newInputStream(Path path, OpenOption... options)
 276         throws IOException
 277     {
 278         return toZipPath(path).newInputStream(options);
 279     }
 280 
 281     @Override
 282     public OutputStream newOutputStream(Path path, OpenOption... options)
 283         throws IOException
 284     {
 285         return toZipPath(path).newOutputStream(options);
 286     }
 287 
 288     @Override
 289     public <A extends BasicFileAttributes> A
 290         readAttributes(Path path, Class<A> type, LinkOption... options)
 291         throws IOException
 292     {
 293         if (type == BasicFileAttributes.class || type == ZipFileAttributes.class)
 294             return (A)toZipPath(path).getAttributes();
 295         return null;
 296     }
 297 
 298     @Override
 299     public Map<String, Object>
 300         readAttributes(Path path, String attribute, LinkOption... options)
 301         throws IOException
 302     {
 303         return toZipPath(path).readAttributes(attribute, options);
 304     }
 305 
 306     @Override
 307     public Path readSymbolicLink(Path link) throws IOException {
 308         throw new UnsupportedOperationException("Not supported.");
 309     }
 310 
 311     @Override
 312     public void setAttribute(Path path, String attribute,
 313                              Object value, LinkOption... options)
 314         throws IOException
 315     {
 316         toZipPath(path).setAttribute(attribute, value, options);
 317     }
 318 
 319     //////////////////////////////////////////////////////////////
 320     void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException {
 321         synchronized (filesystems) {
 322             zfpath = zfpath.toRealPath();
 323             if (filesystems.get(zfpath) == zfs)
 324                 filesystems.remove(zfpath);
 325         }
 326     }
 327 }