8131067: (zipfs) Zip File System Provider returns doubly-encoded Path URIs
authorsherman
Thu, 27 Aug 2015 14:32:42 -0700
changeset 32306 d08fa5944065
parent 32305 5f95864012e6
child 32307 7a3c84a74b56
8131067: (zipfs) Zip File System Provider returns doubly-encoded Path URIs Summary: to decode the zip file path uri before passing it into zippath uri. Reviewed-by: alanb
jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java
jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java
jdk/test/jdk/nio/zipfs/ZipFSTester.java
--- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java	Thu Aug 27 13:22:51 2015 -0700
+++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystemProvider.java	Thu Aug 27 14:32:42 2015 -0700
@@ -65,8 +65,9 @@
             // only support legacy JAR URL syntax  jar:{uri}!/{entry} for now
             String spec = uri.getRawSchemeSpecificPart();
             int sep = spec.indexOf("!/");
-            if (sep != -1)
+            if (sep != -1) {
                 spec = spec.substring(0, sep);
+            }
             return Paths.get(new URI(spec)).toAbsolutePath();
         } catch (URISyntaxException e) {
             throw new IllegalArgumentException(e.getMessage(), e);
@@ -107,6 +108,9 @@
                 // assume NOT a zip/jar file
                 throw new UnsupportedOperationException();
             }
+            if (realPath == null) {  // newly created
+                realPath = path.toRealPath();
+            }
             filesystems.put(realPath, zipfs);
             return zipfs;
         }
@@ -132,7 +136,6 @@
 
     @Override
     public Path getPath(URI uri) {
-
         String spec = uri.getSchemeSpecificPart();
         int sep = spec.indexOf("!/");
         if (sep == -1)
--- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java	Thu Aug 27 13:22:51 2015 -0700
+++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java	Thu Aug 27 14:32:42 2015 -0700
@@ -32,10 +32,10 @@
 import java.nio.file.DirectoryStream.Filter;
 import java.nio.file.attribute.*;
 import java.util.*;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.nio.file.StandardOpenOption.*;
 import static java.nio.file.StandardCopyOption.*;
 
-
 /**
  *
  * @author  Xueming Shen, Rajendra Gutupalli,Jaya Hangal
@@ -180,7 +180,7 @@
     public URI toUri() {
         try {
             return new URI("jar",
-                           zfs.getZipFile().toUri() +
+                           decodeUri(zfs.getZipFile().toUri().toString()) +
                            "!" +
                            zfs.getString(toAbsolutePath().path),
                            null);
@@ -866,4 +866,57 @@
             }
         }
     }
+
+    private static int decode(char c) {
+        if ((c >= '0') && (c <= '9'))
+            return c - '0';
+        if ((c >= 'a') && (c <= 'f'))
+            return c - 'a' + 10;
+        if ((c >= 'A') && (c <= 'F'))
+            return c - 'A' + 10;
+        assert false;
+        return -1;
+    }
+
+    // to avoid double escape
+    static String decodeUri(String s) {
+        if (s == null)
+            return s;
+        int n = s.length();
+        if (n == 0)
+            return s;
+        if (s.indexOf('%') < 0)
+            return s;
+
+        StringBuilder sb = new StringBuilder(n);
+        byte[] bb = new byte[n];
+        boolean betweenBrackets = false;
+
+        for (int i = 0; i < n;) {
+            char c = s.charAt(i);
+            if (c == '[') {
+                betweenBrackets = true;
+            } else if (betweenBrackets && c == ']') {
+                betweenBrackets = false;
+            }
+            if (c != '%' || betweenBrackets ) {
+                sb.append(c);
+                i++;
+                continue;
+            }
+            int nb = 0;
+            while (c == '%') {
+                assert (n - i >= 2);
+                bb[nb++] = (byte)(((decode(s.charAt(++i)) & 0xf) << 4) |
+                                  (decode(s.charAt(++i)) & 0xf));
+                if (++i >= n) {
+                    break;
+                }
+                c = s.charAt(i);
+            }
+            sb.append(new String(bb, 0, nb, UTF_8));
+        }
+        return sb.toString();
+    }
+
 }
--- a/jdk/test/jdk/nio/zipfs/ZipFSTester.java	Thu Aug 27 13:22:51 2015 -0700
+++ b/jdk/test/jdk/nio/zipfs/ZipFSTester.java	Thu Aug 27 14:32:42 2015 -0700
@@ -26,6 +26,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
+import java.net.URLDecoder;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.SeekableByteChannel;
@@ -69,6 +70,7 @@
  * @test
  * @bug 6990846 7009092 7009085 7015391 7014948 7005986 7017840 7007596
  *      7157656 8002390 7012868 7012856 8015728 8038500 8040059 8069211
+ *      8131067
  * @summary Test Zip filesystem provider
  * @run main ZipFSTester
  * @run main/othervm/java.security.policy=test.policy ZipFSTester
@@ -91,6 +93,7 @@
         }
         testTime(jarFile);
         test8069211();
+        test8131067();
     }
 
     static void test0(FileSystem fs)
@@ -441,11 +444,34 @@
         }
     }
 
+    static void test8131067() throws Exception {
+        Map<String, Object> env = new HashMap<String, Object>();
+        env.put("create", "true");
+
+        // file name with space character for URI to quote it
+        File tmp = File.createTempFile("test zipfs", "zip");
+        tmp.delete();    // we need a clean path, no file
+        Path fsPath = tmp.toPath();
+        try (FileSystem fs = newZipFileSystem(fsPath, env);) {
+            Files.write(fs.getPath("/foo"), "hello".getBytes());
+            URI fooUri = fs.getPath("/foo").toUri();
+            if (!Arrays.equals(Files.readAllBytes(Paths.get(fooUri)),
+                               "hello".getBytes())) {
+                throw new RuntimeException("entry close() failed");
+            }
+        } finally {
+            Files.delete(fsPath);
+        }
+    }
+
     private static FileSystem newZipFileSystem(Path path, Map<String, ?> env)
         throws Exception
     {
+        // Use URLDecoder (for test only) to remove the double escaped space
+        // character
         return FileSystems.newFileSystem(
-            new URI("jar", path.toUri().toString(), null), env, null);
+            new URI("jar", URLDecoder.decode(path.toUri().toString(), "utf8"),
+                null), env, null);
     }
 
     private static Path getTempPath() throws IOException