7003155: (fs) Paths.get(<file-uri>) does not handle escaped octets correctly
authoralanb
Mon, 07 Feb 2011 13:55:40 +0000
changeset 8184 526b9179a2bb
parent 8183 06852303ca4e
child 8185 6bf10e071117
child 8186 d0e2cc8b3073
7003155: (fs) Paths.get(<file-uri>) does not handle escaped octets correctly Reviewed-by: sherman
jdk/src/solaris/classes/sun/nio/fs/UnixUriUtils.java
jdk/test/java/nio/file/Path/UriImportExport.java
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixUriUtils.java	Mon Feb 07 13:53:36 2011 +0000
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixUriUtils.java	Mon Feb 07 13:55:40 2011 +0000
@@ -25,8 +25,11 @@
 
 package sun.nio.fs;
 
+import java.nio.file.Path;
+import java.io.File;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Arrays;
 
 /**
  * Unix specific Path <--> URI conversion
@@ -38,7 +41,7 @@
     /**
      * Converts URI to Path
      */
-    static UnixPath fromUri(UnixFileSystem fs, URI uri) {
+    static Path fromUri(UnixFileSystem fs, URI uri) {
         if (!uri.isAbsolute())
             throw new IllegalArgumentException("URI is not absolute");
         if (uri.isOpaque())
@@ -53,22 +56,41 @@
         if (uri.getQuery() != null)
             throw new IllegalArgumentException("URI has a query component");
 
-        String path = uri.getPath();
-        if (path.equals(""))
+        // compatability with java.io.File
+        if (!uri.toString().startsWith("file:///"))
+            return new File(uri).toPath();
+
+        // transformation use raw path
+        String p = uri.getRawPath();
+        int len = p.length();
+        if (len == 0)
             throw new IllegalArgumentException("URI path component is empty");
-        if (path.endsWith("/") && (path.length() > 1)) {
-            // "/foo/" --> "/foo", but "/" --> "/"
-            path = path.substring(0, path.length() - 1);
-        }
 
-        // preserve bytes
-        byte[] result = new byte[path.length()];
-        for (int i=0; i<path.length(); i++) {
-            byte v = (byte)(path.charAt(i));
-            if (v == 0)
-                throw new IllegalArgumentException("Nul character not allowed");
-            result[i] = v;
+        // transform escaped octets and unescaped characters to bytes
+        if (p.endsWith("/") && len > 1)
+            len--;
+        byte[] result = new byte[len];
+        int rlen = 0;
+        int pos = 0;
+        while (pos < len) {
+            char c = p.charAt(pos++);
+            byte b;
+            if (c == '%') {
+                assert (pos+2) <= len;
+                char c1 = p.charAt(pos++);
+                char c2 = p.charAt(pos++);
+                b = (byte)((decode(c1) << 4) | decode(c2));
+                if (b == 0)
+                    throw new IllegalArgumentException("Nul character not allowed");
+            } else {
+                assert c < 0x80;
+                b = (byte)c;
+            }
+            result[rlen++] = b;
         }
+        if (rlen != result.length)
+            result = Arrays.copyOf(result, rlen);
+
         return new UnixPath(fs, result);
     }
 
@@ -86,7 +108,7 @@
             } else {
                sb.append('%');
                sb.append(hexDigits[(c >> 4) & 0x0f]);
-               sb.append(hexDigits[(c >> 0) & 0x0f]);
+               sb.append(hexDigits[(c) & 0x0f]);
             }
         }
 
@@ -164,6 +186,17 @@
         return false;
     }
 
+    // decode
+    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;
+        throw new AssertionError();
+    }
+
     // digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
     //            "8" | "9"
     private static final long L_DIGIT = lowMask('0', '9');
--- a/jdk/test/java/nio/file/Path/UriImportExport.java	Mon Feb 07 13:53:36 2011 +0000
+++ b/jdk/test/java/nio/file/Path/UriImportExport.java	Mon Feb 07 13:55:40 2011 +0000
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 4313887
+ * @bug 4313887 7003155
  * @summary Unit test for java.nio.file.Path
  */
 
@@ -36,42 +36,105 @@
     static final PrintStream log = System.out;
     static int failures = 0;
 
-    static void test(String fn, String expected) {
+    /**
+     * Test Path -> URI -> Path
+     */
+    static void testPath(String s) {
+        Path path = Paths.get(s);
+        log.println(path);
+        URI uri = path.toUri();
+        log.println("  --> " + uri);
+        Path result = Paths.get(uri);
+        log.println("  --> " + result);
+        if (!result.equals(path.toAbsolutePath())) {
+            log.println("FAIL: Expected " + path + ", got " + result);
+            failures++;
+        }
         log.println();
-        Path p = Paths.get(fn);
-        log.println(p);
-        URI u = p.toUri();
-        log.println("  --> " + u);
-        if (expected != null && !(u.toString().equals(expected))) {
-            log.println("FAIL: Expected " + expected);
-            failures++;
-            return;
-        }
-        Path q = Paths.get(u);
-        log.println("  --> " + q);
-        if (!p.toAbsolutePath().equals(q)) {
-            log.println("FAIL: Expected " + p + ", got " + q);
+    }
+
+    /**
+     * Test Path -> (expected) URI -> Path
+     */
+    static void testPath(String s, String expectedUri) {
+        Path path = Paths.get(s);
+        log.println(path);
+        URI uri = path.toUri();
+        log.println("  --> " + uri);
+        if (!uri.toString().equals(expectedUri)) {
+            log.println("FAILED: Expected " + expectedUri + ", got " + uri);
             failures++;
             return;
         }
+        Path result = Paths.get(uri);
+        log.println("  --> " + result);
+        if (!result.equals(path.toAbsolutePath())) {
+            log.println("FAIL: Expected " + path + ", got " + result);
+            failures++;
+        }
+        log.println();
     }
 
-    static void test(String fn) {
-        test(fn, null);
+    /**
+     * Test URI -> Path -> URI
+     */
+    static void testUri(String s) throws Exception {
+        URI uri = URI.create(s);
+        log.println(uri);
+        Path path = Paths.get(uri);
+        log.println("  --> " + path);
+        URI result = path.toUri();
+        log.println("  --> " + result);
+        if (!result.equals(uri)) {
+            log.println("FAIL: Expected " + uri + ", got " + result);
+            failures++;
+        }
+        log.println();
+    }
+
+    /**
+     * Test URI -> Path fails with IllegalArgumentException
+     */
+    static void testBadUri(String s) throws Exception {
+        URI uri = URI.create(s);
+        log.println(uri);
+        try {
+            Path path = Paths.get(uri);
+            log.format(" --> %s  FAIL: Expected IllegalArgumentException\n", path);
+            failures++;
+        } catch (IllegalArgumentException expected) {
+            log.println("  --> IllegalArgumentException (expected)");
+        }
+        log.println();
     }
 
     public static void main(String[] args) throws Exception {
-        test("foo");
-        test("/foo");
-        test("/foo bar");
+        testBadUri("file:foo");
+        testBadUri("file:/foo?q");
+        testBadUri("file:/foo#f");
 
         String osname = System.getProperty("os.name");
         if (osname.startsWith("Windows")) {
-            test("C:\\foo");
-            test("C:foo");
-            test("\\\\rialto.dublin.com\\share\\");
-            test("\\\\fe80--203-baff-fe5a-749ds1.ipv6-literal.net\\share\\missing",
+            testPath("C:\\doesnotexist");
+            testPath("C:doesnotexist");
+            testPath("\\\\server.nowhere.oracle.com\\share\\");
+            testPath("\\\\fe80--203-baff-fe5a-749ds1.ipv6-literal.net\\share\\missing",
                 "file://[fe80::203:baff:fe5a:749d%1]/share/missing");
+        } else {
+            testPath("doesnotexist");
+            testPath("/doesnotexist");
+            testPath("/does not exist");
+            testUri("file:///");
+            testUri("file:///foo/bar/doesnotexist");
+            testUri("file:/foo/bar/doesnotexist");
+
+            // file:///foo/bar/\u0440\u0443\u0441\u0441\u043A\u0438\u0439 (Russian)
+            testUri("file:///foo/bar/%D1%80%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9");
+
+            // invalid
+            testBadUri("file:foo");
+            testBadUri("file://server/foo");
+            testBadUri("file:///foo%00");
         }
 
         if (failures > 0)