8164705: Remove pathname canonicalization from FilePermission
authorweijun
Mon, 10 Oct 2016 08:28:50 +0800
changeset 41377 271ee055cb31
parent 41376 0908484888a7
child 41378 62969f0fb9c5
8164705: Remove pathname canonicalization from FilePermission Reviewed-by: alanb, bpb
jdk/src/java.base/share/classes/java/io/FilePermission.java
jdk/src/java.base/share/classes/java/security/AccessControlContext.java
jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
jdk/src/java.base/share/classes/jdk/internal/misc/JavaIOFilePermissionAccess.java
jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java
jdk/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java
jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java
jdk/src/java.base/share/classes/sun/security/util/FilePermCompat.java
jdk/test/java/io/FilePermission/Correctness.java
jdk/test/java/io/FilePermission/FilePermissionCollection.java
jdk/test/java/io/FilePermission/ReadFileOnPath.java
jdk/test/java/io/FilePermission/m/module-info.java
jdk/test/java/io/FilePermission/m/p/App.java
jdk/test/java/net/URLClassLoader/getresourceasstream/Test.java
jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh
jdk/test/java/security/testlibrary/Proc.java
jdk/test/sun/security/provider/PolicyFile/CanonPath.java
jdk/test/sun/security/util/FilePermCompat/CompatImpact.java
jdk/test/sun/security/util/FilePermCompat/Flag.java
--- a/jdk/src/java.base/share/classes/java/io/FilePermission.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/io/FilePermission.java	Mon Oct 10 08:28:50 2016 +0800
@@ -25,11 +25,20 @@
 
 package java.io;
 
+import java.net.URI;
+import java.nio.file.*;
 import java.security.*;
 import java.util.Enumeration;
+import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.Vector;
 import java.util.concurrent.ConcurrentHashMap;
+
+import jdk.internal.misc.JavaIOFilePermissionAccess;
+import jdk.internal.misc.SharedSecrets;
+import sun.nio.fs.DefaultFileSystemProvider;
+import sun.security.action.GetPropertyAction;
+import sun.security.util.FilePermCompat;
 import sun.security.util.SecurityConstants;
 
 /**
@@ -41,8 +50,11 @@
  * the file separator character, <code>File.separatorChar</code>) indicates
  * all the files and directories contained in that directory. A pathname
  * that ends with "/-" indicates (recursively) all files
- * and subdirectories contained in that directory. A pathname consisting of
- * the special token "&lt;&lt;ALL FILES&gt;&gt;" matches <b>any</b> file.
+ * and subdirectories contained in that directory. Such a pathname is called
+ * a wildcard pathname. Otherwise, it's a simple pathname.
+ * <P>
+ * A pathname consisting of the special token {@literal "<<ALL FILES>>"}
+ * matches <b>any</b> file.
  * <P>
  * Note: A pathname consisting of a single "*" indicates all the files
  * in the current directory, while a pathname consisting of a single "-"
@@ -75,12 +87,12 @@
  * <P>
  * Be careful when granting FilePermissions. Think about the implications
  * of granting read and especially write access to various files and
- * directories. The "&lt;&lt;ALL FILES&gt;&gt;" permission with write action is
+ * directories. The {@literal "<<ALL FILES>>"} permission with write action is
  * especially dangerous. This grants permission to write to the entire
  * file system. One thing this effectively allows is replacement of the
  * system binary, including the JVM runtime environment.
- *
- * <p>Please note: Code can always read a file from the same
+ * <P>
+ * Please note: Code can always read a file from the same
  * directory it's in (or a subdirectory of that directory); it does not
  * need explicit permission to do so.
  *
@@ -145,34 +157,127 @@
     private String actions; // Left null as long as possible, then
                             // created and re-used in the getAction function.
 
-    // canonicalized dir path. In the case of
-    // directories, it is the name "/blah/*" or "/blah/-" without
-    // the last character (the "*" or "-").
+    // canonicalized dir path. used by the "old" behavior (nb == false).
+    // In the case of directories, it is the name "/blah/*" or "/blah/-"
+    // without the last character (the "*" or "-").
 
     private transient String cpath;
 
+    // Following fields used by the "new" behavior (nb == true), in which
+    // input path is not canonicalized. For compatibility (so that granting
+    // FilePermission on "x" allows reading "`pwd`/x", an alternative path
+    // can be added so that both can be used in an implies() check. Please note
+    // the alternative path only deals with absolute/relative path, and does
+    // not deal with symlink/target.
+
+    private transient Path npath;       // normalized dir path.
+    private transient Path npath2;      // alternative normalized dir path.
+    private transient boolean allFiles; // whether this is <<ALL FILES>>
+
     // static Strings used by init(int mask)
     private static final char RECURSIVE_CHAR = '-';
     private static final char WILD_CHAR = '*';
 
-/*
-    public String toString()
-    {
-        StringBuffer sb = new StringBuffer();
-        sb.append("***\n");
-        sb.append("cpath = "+cpath+"\n");
-        sb.append("mask = "+mask+"\n");
-        sb.append("actions = "+getActions()+"\n");
-        sb.append("directory = "+directory+"\n");
-        sb.append("recursive = "+recursive+"\n");
-        sb.append("***\n");
-        return sb.toString();
-    }
-*/
+//    public String toString() {
+//        StringBuffer sb = new StringBuffer();
+//        sb.append("*** FilePermission on " + getName() + " ***");
+//        for (Field f : FilePermission.class.getDeclaredFields()) {
+//            if (!Modifier.isStatic(f.getModifiers())) {
+//                try {
+//                    sb.append(f.getName() + " = " + f.get(this));
+//                } catch (Exception e) {
+//                    sb.append(f.getName() + " = " + e.toString());
+//                }
+//                sb.append('\n');
+//            }
+//        }
+//        sb.append("***\n");
+//        return sb.toString();
+//    }
 
     private static final long serialVersionUID = 7930732926638008763L;
 
     /**
+     * Always use the internal default file system, in case it was modified
+     * with java.nio.file.spi.DefaultFileSystemProvider.
+     */
+    private static final java.nio.file.FileSystem builtInFS =
+            DefaultFileSystemProvider.create()
+                    .getFileSystem(URI.create("file:///"));
+
+    /**
+     * Creates FilePermission objects with special internals.
+     * See {@link FilePermCompat#newPermPlusAltPath(Permission)} and
+     * {@link FilePermCompat#newPermUsingAltPath(Permission)}.
+     */
+
+    private static final Path here = builtInFS.getPath(
+            GetPropertyAction.privilegedGetProperty("user.dir"));
+
+    /**
+     * A private constructor like a clone, only npath2 is not touched.
+     * @param input
+     */
+    private FilePermission(FilePermission input) {
+        super(input.getName());
+        this.npath = input.npath;
+        this.actions = input.actions;
+        this.allFiles = input.allFiles;
+        this.recursive = input.recursive;
+        this.directory = input.directory;
+        this.cpath = input.cpath;
+        this.mask = input.mask;
+    }
+
+    /**
+     * Returns the alternative path as a Path object, i.e. absolute path
+     * for a relative one, or vice versa.
+     *
+     * @param in a real path w/o "-" or "*" at the end, and not <<ALL FILES>>.
+     * @return the alternative path, or null if cannot find one.
+     */
+    private static Path altPath(Path in) {
+        try {
+            if (!in.isAbsolute()) {
+                return here.resolve(in).normalize();
+            } else {
+                return here.relativize(in).normalize();
+            }
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    static {
+        SharedSecrets.setJavaIOFilePermissionAccess(
+            new JavaIOFilePermissionAccess() {
+                public FilePermission newPermPlusAltPath(FilePermission input) {
+                    if (input.npath2 == null && !input.allFiles) {
+                        Path npath2 = altPath(input.npath);
+                        if (npath2 != null) {
+                            FilePermission np = new FilePermission(input);
+                            np.npath2 = npath2;
+                            return np;
+                        }
+                    }
+                    return input;
+                }
+                public FilePermission newPermUsingAltPath(FilePermission input) {
+                    if (!input.allFiles) {
+                        Path npath2 = altPath(input.npath);
+                        if (npath2 != null) {
+                            FilePermission np = new FilePermission(input);
+                            np.npath = npath2;
+                            return np;
+                        }
+                    }
+                    return null;
+                }
+            }
+        );
+    }
+
+    /**
      * initialize a FilePermission object. Common to all constructors.
      * Also called during de-serialization.
      *
@@ -186,60 +291,106 @@
         if (mask == NONE)
                 throw new IllegalArgumentException("invalid actions mask");
 
-        if ((cpath = getName()) == null)
+        if (FilePermCompat.nb) {
+            String name = getName();
+
+            if (name == null)
+                throw new NullPointerException("name can't be null");
+
+            this.mask = mask;
+
+            if (name.equals("<<ALL FILES>>")) {
+                allFiles = true;
+                npath = builtInFS.getPath("");
+                // other fields remain default
+                return;
+            }
+
+            boolean rememberStar = false;
+            if (name.endsWith("*")) {
+                rememberStar = true;
+                recursive = false;
+                name = name.substring(0, name.length()-1) + "-";
+            }
+
+            try {
+                // new File() can "normalize" some name, for example, "/C:/X" on
+                // Windows. Some JDK codes generate such illegal names.
+                npath = builtInFS.getPath(new File(name).getPath())
+                        .normalize();
+            } catch (InvalidPathException ipe) {
+                // Still invalid. For compatibility reason, accept it
+                // but make this permission useless.
+                npath = builtInFS.getPath("-u-s-e-l-e-s-s-");
+                this.mask = NONE;
+            }
+
+            // lastName should always be non-null now
+            Path lastName = npath.getFileName();
+            if (lastName != null && lastName.toString().equals("-")) {
+                directory = true;
+                recursive = !rememberStar;
+                npath = npath.getParent();
+            }
+            if (npath == null) {
+                npath = builtInFS.getPath("");
+            }
+        } else {
+            if ((cpath = getName()) == null)
                 throw new NullPointerException("name can't be null");
 
-        this.mask = mask;
+            this.mask = mask;
 
-        if (cpath.equals("<<ALL FILES>>")) {
-            directory = true;
-            recursive = true;
-            cpath = "";
-            return;
-        }
+            if (cpath.equals("<<ALL FILES>>")) {
+                directory = true;
+                recursive = true;
+                cpath = "";
+                return;
+            }
 
-        // store only the canonical cpath if possible
-        cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
-            public String run() {
-                try {
-                    String path = cpath;
-                    if (cpath.endsWith("*")) {
-                        // call getCanonicalPath with a path with wildcard character
-                        // replaced to avoid calling it with paths that are
-                        // intended to match all entries in a directory
-                        path = path.substring(0, path.length()-1) + "-";
-                        path = new File(path).getCanonicalPath();
-                        return path.substring(0, path.length()-1) + "*";
-                    } else {
-                        return new File(path).getCanonicalPath();
+            // store only the canonical cpath if possible
+            cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
+                public String run() {
+                    try {
+                        String path = cpath;
+                        if (cpath.endsWith("*")) {
+                            // call getCanonicalPath with a path with wildcard character
+                            // replaced to avoid calling it with paths that are
+                            // intended to match all entries in a directory
+                            path = path.substring(0, path.length() - 1) + "-";
+                            path = new File(path).getCanonicalPath();
+                            return path.substring(0, path.length() - 1) + "*";
+                        } else {
+                            return new File(path).getCanonicalPath();
+                        }
+                    } catch (IOException ioe) {
+                        return cpath;
                     }
-                } catch (IOException ioe) {
-                    return cpath;
                 }
-            }
-        });
+            });
 
-        int len = cpath.length();
-        char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
+            int len = cpath.length();
+            char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
 
-        if (last == RECURSIVE_CHAR &&
-            cpath.charAt(len - 2) == File.separatorChar) {
-            directory = true;
-            recursive = true;
-            cpath = cpath.substring(0, --len);
-        } else if (last == WILD_CHAR &&
-            cpath.charAt(len - 2) == File.separatorChar) {
-            directory = true;
-            //recursive = false;
-            cpath = cpath.substring(0, --len);
-        } else {
-            // overkill since they are initialized to false, but
-            // commented out here to remind us...
-            //directory = false;
-            //recursive = false;
+            if (last == RECURSIVE_CHAR &&
+                    cpath.charAt(len - 2) == File.separatorChar) {
+                directory = true;
+                recursive = true;
+                cpath = cpath.substring(0, --len);
+            } else if (last == WILD_CHAR &&
+                    cpath.charAt(len - 2) == File.separatorChar) {
+                directory = true;
+                //recursive = false;
+                cpath = cpath.substring(0, --len);
+            } else {
+                // overkill since they are initialized to false, but
+                // commented out here to remind us...
+                //directory = false;
+                //recursive = false;
+            }
+
+            // XXX: at this point the path should be absolute. die if it isn't?
         }
-
-        // XXX: at this point the path should be absolute. die if it isn't?
     }
 
     /**
@@ -254,7 +405,7 @@
      * indicates all the files and directories contained in that directory.
      * A pathname that ends with "/-" indicates (recursively) all files and
      * subdirectories contained in that directory. The special pathname
-     * "&lt;&lt;ALL FILES&gt;&gt;" matches any file.
+     * {@literal "<<ALL FILES>>"} matches any file.
      *
      * <p>A pathname consisting of a single "*" indicates all the files
      * in the current directory, while a pathname consisting of a single "-"
@@ -264,6 +415,28 @@
      *
      * <p>A pathname containing an empty string represents an empty path.
      *
+     * @implNote In this implementation, the
+     * {@code jdk.io.permissionsUseCanonicalPath} system property dictates how
+     * the {@code path} argument is processed and stored.
+     * <P>
+     * If the value of the system property is set to {@code true}, {@code path}
+     * is canonicalized and stored as a String object named {@code cpath}.
+     * This means a relative path is converted to an absolute path, a Windows
+     * DOS-style 8.3 path is expanded to a long path, and a symbolic link is
+     * resolved to its target, etc.
+     * <P>
+     * If the value of the system property is set to {@code false}, {@code path}
+     * is converted to a {@link java.nio.file.Path} object named {@code npath}
+     * after {@link Path#normalize() normalization}. No canonicalization is
+     * performed which means the underlying file system is not accessed.
+     * <P>
+     * In either case, the "*" or "-" character at the end of a wildcard
+     * {@code path} is removed before canonicalization or normalization.
+     * It is stored in a separate wildcard flag field.
+     * <P>
+     * The default value of the {@code jdk.io.permissionsUseCanonicalPath}
+     * system property is {@code false} in this implementation.
+     *
      * @param path the pathname of the file/directory.
      * @param actions the action string.
      *
@@ -305,6 +478,38 @@
      *      "/tmp/*" encompasses all files in the "/tmp" directory,
      *      including the one named "foo".
      * </ul>
+     * <P>
+     * Precisely, a simple pathname implies another simple pathname
+     * if and only if they are equal. A simple pathname never implies
+     * a wildcard pathname. A wildcard pathname implies another wildcard
+     * pathname if and only if all simple pathnames implied by the latter
+     * are implied by the former. A wildcard pathname implies a simple
+     * pathname if and only if
+     * <ul>
+     *     <li>if the wildcard flag is "*", the simple pathname's path
+     *     must be right inside the wildcard pathname's path.
+     *     <li>if the wildcard flag is "-", the simple pathname's path
+     *     must be recursively inside the wildcard pathname's path.
+     * </ul>
+     * <P>
+     * {@literal "<<ALL FILES>>"} implies every other pathname. No pathname,
+     * except for {@literal "<<ALL FILES>>"} itself, implies
+     * {@literal "<<ALL FILES>>"}.
+     *
+     * @implNote
+     * If {@code jdk.io.permissionsUseCanonicalPath} is {@code true}, a
+     * simple {@code cpath} is inside a wildcard {@code cpath} if and only if
+     * after removing the base name (the last name in the pathname's name
+     * sequence) from the former the remaining part equals to the latter,
+     * a simple {@code cpath} is recursively inside a wildcard {@code cpath}
+     * if and only if the former starts with the latter.
+     * <p>
+     * If {@code jdk.io.permissionsUseCanonicalPath} is {@code false}, a
+     * simple {@code npath} is inside a wildcard {@code npath} if and only if
+     * {@code  simple_npath.relativize(wildcard_npath)} is exactly "..",
+     * a simple {@code npath} is recursively inside a wildcard {@code npath}
+     * if and only if {@code simple_npath.relativize(wildcard_npath)}
+     * is a series of one or more "..".
      *
      * @param p the permission to check against.
      *
@@ -334,45 +539,125 @@
      * @return the effective mask
      */
     boolean impliesIgnoreMask(FilePermission that) {
-        if (this.directory) {
-            if (this.recursive) {
-                // make sure that.path is longer then path so
-                // something like /foo/- does not imply /foo
-                if (that.directory) {
-                    return (that.cpath.length() >= this.cpath.length()) &&
-                            that.cpath.startsWith(this.cpath);
-                }  else {
-                    return ((that.cpath.length() > this.cpath.length()) &&
-                        that.cpath.startsWith(this.cpath));
+        if (FilePermCompat.nb) {
+            if (allFiles) {
+                return true;
+            }
+            if (that.allFiles) {
+                return false;
+            }
+            // Left at least same level of wildness as right
+            if ((this.recursive && that.recursive) != that.recursive
+                    || (this.directory && that.directory) != that.directory) {
+                return false;
+            }
+            // Same npath is good as long as both or neither are directories
+            if (this.npath.equals(that.npath)
+                    && this.directory == that.directory) {
+                return true;
+            }
+            int diff = containsPath(this.npath, that.npath);
+            // Right inside left is good if recursive
+            if (diff >= 1 && recursive) {
+                return true;
+            }
+            // Right right inside left if it is element in set
+            if (diff == 1 && directory && !that.directory) {
+                return true;
+            }
+
+            // Hack: if a npath2 field exists, apply the same checks
+            // on it as a fallback.
+            if (this.npath2 != null) {
+                if (this.npath2.equals(that.npath)
+                        && this.directory == that.directory) {
+                    return true;
+                }
+                diff = containsPath(this.npath2, that.npath);
+                if (diff >= 1 && recursive) {
+                    return true;
                 }
-            } else {
-                if (that.directory) {
-                    // if the permission passed in is a directory
-                    // specification, make sure that a non-recursive
-                    // permission (i.e., this object) can't imply a recursive
-                    // permission.
-                    if (that.recursive)
-                        return false;
-                    else
-                        return (this.cpath.equals(that.cpath));
+                if (diff == 1 && directory && !that.directory) {
+                    return true;
+                }
+            }
+
+            return false;
+        } else {
+            if (this.directory) {
+                if (this.recursive) {
+                    // make sure that.path is longer then path so
+                    // something like /foo/- does not imply /foo
+                    if (that.directory) {
+                        return (that.cpath.length() >= this.cpath.length()) &&
+                                that.cpath.startsWith(this.cpath);
+                    } else {
+                        return ((that.cpath.length() > this.cpath.length()) &&
+                                that.cpath.startsWith(this.cpath));
+                    }
                 } else {
-                    int last = that.cpath.lastIndexOf(File.separatorChar);
-                    if (last == -1)
-                        return false;
-                    else {
-                        // this.cpath.equals(that.cpath.substring(0, last+1));
-                        // Use regionMatches to avoid creating new string
-                        return (this.cpath.length() == (last + 1)) &&
-                            this.cpath.regionMatches(0, that.cpath, 0, last+1);
+                    if (that.directory) {
+                        // if the permission passed in is a directory
+                        // specification, make sure that a non-recursive
+                        // permission (i.e., this object) can't imply a recursive
+                        // permission.
+                        if (that.recursive)
+                            return false;
+                        else
+                            return (this.cpath.equals(that.cpath));
+                    } else {
+                        int last = that.cpath.lastIndexOf(File.separatorChar);
+                        if (last == -1)
+                            return false;
+                        else {
+                            // this.cpath.equals(that.cpath.substring(0, last+1));
+                            // Use regionMatches to avoid creating new string
+                            return (this.cpath.length() == (last + 1)) &&
+                                    this.cpath.regionMatches(0, that.cpath, 0, last + 1);
+                        }
                     }
                 }
+            } else if (that.directory) {
+                // if this is NOT recursive/wildcarded,
+                // do not let it imply a recursive/wildcarded permission
+                return false;
+            } else {
+                return (this.cpath.equals(that.cpath));
             }
-        } else if (that.directory) {
-            // if this is NOT recursive/wildcarded,
-            // do not let it imply a recursive/wildcarded permission
-            return false;
-        } else {
-            return (this.cpath.equals(that.cpath));
+        }
+    }
+
+    /**
+     * Returns the depth between an outer path p1 and an inner path p2. -1
+     * is returned if
+     *
+     * - p1 does not contains p2.
+     * - this is not decidable. For example, p1="../x", p2="y".
+     * - the depth is not decidable. For example, p1="/", p2="x".
+     *
+     * This method can return 2 if the depth is greater than 2.
+     *
+     * @param p1 the expected outer path, normalized
+     * @param p2 the expected inner path, normalized
+     * @return the depth in between
+     */
+    private static int containsPath(Path p1, Path p2) {
+        Path p;
+        try {
+            p = p2.relativize(p1).normalize();
+            if (p.getName(0).toString().isEmpty()) {
+                return 0;
+            } else {
+                for (Path item: p) {
+                    String s = item.toString();
+                    if (!s.equals("..")) {
+                        return -1;
+                    }
+                }
+                return p.getNameCount();
+            }
+        } catch (IllegalArgumentException iae) {
+            return -1;
         }
     }
 
@@ -380,6 +665,12 @@
      * Checks two FilePermission objects for equality. Checks that <i>obj</i> is
      * a FilePermission, and has the same pathname and actions as this object.
      *
+     * @implNote More specifically, two pathnames are the same if and only if
+     * they have the same wildcard flag and their {@code cpath}
+     * (if {@code jdk.io.permissionsUseCanonicalPath} is {@code true}) or
+     * {@code npath} (if {@code jdk.io.permissionsUseCanonicalPath}
+     * is {@code false}) are equal. Or they are both {@literal "<<ALL FILES>>"}.
+     *
      * @param obj the object we are testing for equality with this object.
      * @return <code>true</code> if obj is a FilePermission, and has the same
      *          pathname and actions as this FilePermission object,
@@ -395,10 +686,18 @@
 
         FilePermission that = (FilePermission) obj;
 
-        return (this.mask == that.mask) &&
-            this.cpath.equals(that.cpath) &&
-            (this.directory == that.directory) &&
-            (this.recursive == that.recursive);
+        if (FilePermCompat.nb) {
+            return (this.mask == that.mask) &&
+                    (this.allFiles == that.allFiles) &&
+                    this.npath.equals(that.npath) &&
+                    (this.directory == that.directory) &&
+                    (this.recursive == that.recursive);
+        } else {
+            return (this.mask == that.mask) &&
+                    this.cpath.equals(that.cpath) &&
+                    (this.directory == that.directory) &&
+                    (this.recursive == that.recursive);
+        }
     }
 
     /**
@@ -408,7 +707,11 @@
      */
     @Override
     public int hashCode() {
-        return 0;
+        if (FilePermCompat.nb) {
+            return Objects.hash(mask, allFiles, directory, recursive, npath);
+        } else {
+            return 0;
+        }
     }
 
     /**
--- a/jdk/src/java.base/share/classes/java/security/AccessControlContext.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/security/AccessControlContext.java	Mon Oct 10 08:28:50 2016 +0800
@@ -27,7 +27,9 @@
 
 import java.util.ArrayList;
 import java.util.List;
+
 import sun.security.util.Debug;
+import sun.security.util.FilePermCompat;
 import sun.security.util.SecurityConstants;
 
 
@@ -175,7 +177,7 @@
 
     /**
      * package private to allow calls from ProtectionDomain without performing
-     * the security check for {@linkplain SecurityConstants.CREATE_ACC_PERMISSION}
+     * the security check for {@linkplain SecurityConstants#CREATE_ACC_PERMISSION}
      * permission
      */
     AccessControlContext(AccessControlContext acc,
@@ -253,7 +255,8 @@
                 if (perms[i].getClass() == AllPermission.class) {
                     parent = null;
                 }
-                tmp[i] = perms[i];
+                // Add altPath into permission for compatibility.
+                tmp[i] = FilePermCompat.newPermPlusAltPath(perms[i]);
             }
         }
 
@@ -443,7 +446,7 @@
         }
 
         for (int i=0; i< context.length; i++) {
-            if (context[i] != null &&  !context[i].implies(perm)) {
+            if (context[i] != null && !context[i].impliesWithAltFilePerm(perm)) {
                 if (dumpDebug) {
                     debug.println("access denied " + perm);
                 }
--- a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java	Mon Oct 10 08:28:50 2016 +0800
@@ -32,13 +32,14 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import jdk.internal.misc.JavaSecurityAccess;
 import jdk.internal.misc.JavaSecurityProtectionDomainAccess;
 import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
 import jdk.internal.misc.SharedSecrets;
+import sun.security.provider.PolicyFile;
 import sun.security.util.Debug;
+import sun.security.util.FilePermCompat;
 import sun.security.util.SecurityConstants;
 
 /**
@@ -303,11 +304,71 @@
         }
 
         if (!staticPermissions &&
-            Policy.getPolicyNoCheck().implies(this, perm))
+            Policy.getPolicyNoCheck().implies(this, perm)) {
+            return true;
+        }
+        if (permissions != null) {
+            return permissions.implies(perm);
+        }
+
+        return false;
+    }
+
+    /**
+     * This method has the same logic flow as {@link #implies} except that
+     * when the {@link FilePermCompat#compat} flag is on it ensures
+     * FilePermission compatibility after JDK-8164705. {@code implies()}
+     * is called when compat flag is not on or user has extended
+     * {@code ProtectionDomain}.
+     *
+     * This method is called by {@link AccessControlContext#checkPermission}
+     * and not intended to be called by an application.
+     */
+    boolean impliesWithAltFilePerm(Permission perm) {
+
+        // If this is a subclass of ProtectionDomain. Call the old method.
+        if (!FilePermCompat.compat || getClass() != ProtectionDomain.class) {
+            return implies(perm);
+        }
+
+        if (hasAllPerm) {
+            // internal permission collection already has AllPermission -
+            // no need to go to policy
             return true;
-        if (permissions != null)
-            return permissions.implies(perm);
+        }
+
+        Permission p2 = null;
+        boolean p2Calculated = false;
 
+        if (!staticPermissions) {
+            Policy policy = Policy.getPolicyNoCheck();
+            if (policy instanceof PolicyFile) {
+                // The PolicyFile implementation supports compatibility
+                // inside and it also covers the static permissions.
+                return policy.implies(this, perm);
+            } else {
+                if (policy.implies(this, perm)) {
+                    return true;
+                }
+                p2 = FilePermCompat.newPermUsingAltPath(perm);
+                p2Calculated = true;
+                if (p2 != null && policy.implies(this, p2)) {
+                    return true;
+                }
+            }
+        }
+        if (permissions != null) {
+            if (permissions.implies(perm)) {
+                return true;
+            } else {
+                if (!p2Calculated) {
+                    p2 = FilePermCompat.newPermUsingAltPath(perm);
+                }
+                if (p2 != null) {
+                    return permissions.implies(p2);
+                }
+            }
+        }
         return false;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaIOFilePermissionAccess.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.misc;
+
+import java.io.FilePermission;
+
+public interface JavaIOFilePermissionAccess {
+
+    /**
+     * Returns a new FilePermission plus an alternative path.
+     *
+     * @param input the input
+     * @return the new FilePermission plus the alt path (as npath2)
+     *         or the input itself if no alt path is available.
+     */
+    FilePermission newPermPlusAltPath(FilePermission input);
+
+    /**
+     * Returns a new FilePermission using an alternative path.
+     *
+     * @param input the input
+     * @return the new FilePermission using the alt path (as npath)
+     *         or null if no alt path is available
+     */
+    FilePermission newPermUsingAltPath(FilePermission input);
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java	Mon Oct 10 08:28:50 2016 +0800
@@ -29,6 +29,7 @@
 import java.util.jar.JarFile;
 import java.io.Console;
 import java.io.FileDescriptor;
+import java.io.FilePermission;
 import java.io.ObjectInputStream;
 import java.io.RandomAccessFile;
 import java.security.ProtectionDomain;
@@ -58,6 +59,7 @@
     private static JavaNetSocketAccess javaNetSocketAccess;
     private static JavaNioAccess javaNioAccess;
     private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
+    private static JavaIOFilePermissionAccess javaIOFilePermissionAccess;
     private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess;
     private static JavaSecurityAccess javaSecurityAccess;
     private static JavaUtilZipFileAccess javaUtilZipFileAccess;
@@ -201,6 +203,17 @@
         javaIOFileDescriptorAccess = jiofda;
     }
 
+    public static JavaIOFilePermissionAccess getJavaIOFilePermissionAccess() {
+        if (javaIOFilePermissionAccess == null)
+            unsafe.ensureClassInitialized(FilePermission.class);
+
+        return javaIOFilePermissionAccess;
+    }
+
+    public static void setJavaIOFilePermissionAccess(JavaIOFilePermissionAccess jiofpa) {
+        javaIOFilePermissionAccess = jiofpa;
+    }
+
     public static JavaIOFileDescriptorAccess getJavaIOFileDescriptorAccess() {
         if (javaIOFileDescriptorAccess == null)
             unsafe.ensureClassInitialized(FileDescriptor.class);
--- a/jdk/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java	Mon Oct 10 08:28:50 2016 +0800
@@ -41,10 +41,6 @@
 import java.util.*;
 import java.text.SimpleDateFormat;
 
-import sun.security.action.GetPropertyAction;
-import sun.security.action.GetIntegerAction;
-import sun.security.action.GetBooleanAction;
-
 public class FileURLConnection extends URLConnection {
 
     static String CONTENT_LENGTH = "content-length";
@@ -224,8 +220,13 @@
             if (File.separatorChar == '/') {
                 permission = new FilePermission(decodedPath, "read");
             } else {
+                // decode could return /c:/x/y/z.
+                if (decodedPath.length() > 2 && decodedPath.charAt(0) == '/'
+                        && decodedPath.charAt(2) == ':') {
+                    decodedPath = decodedPath.substring(1);
+                }
                 permission = new FilePermission(
-                        decodedPath.replace('/',File.separatorChar), "read");
+                        decodedPath.replace('/', File.separatorChar), "read");
             }
         }
         return permission;
--- a/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Mon Oct 10 08:28:50 2016 +0800
@@ -45,11 +45,7 @@
 import jdk.internal.misc.JavaSecurityProtectionDomainAccess;
 import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
 import jdk.internal.misc.SharedSecrets;
-import sun.security.util.PolicyUtil;
-import sun.security.util.PropertyExpander;
-import sun.security.util.Debug;
-import sun.security.util.ResourcesMgr;
-import sun.security.util.SecurityConstants;
+import sun.security.util.*;
 import sun.net.www.ParseUtil;
 
 /**
@@ -534,8 +530,6 @@
     /**
      * Reads a policy configuration into the Policy object using a
      * Reader object.
-     *
-     * @param policyFile the policy Reader object.
      */
     private boolean init(URL policy, PolicyInfo newInfo, boolean defPolicy) {
 
@@ -1099,7 +1093,7 @@
             synchronized (pc) {
                 Enumeration<Permission> e = pc.elements();
                 while (e.hasMoreElements()) {
-                    perms.add(e.nextElement());
+                    perms.add(FilePermCompat.newPermPlusAltPath(e.nextElement()));
                 }
             }
         }
@@ -1127,7 +1121,7 @@
      * object with additional permissions granted to the specified
      * ProtectionDomain.
      *
-     * @param perm the Permissions to populate
+     * @param perms the Permissions to populate
      * @param pd the ProtectionDomain associated with the caller.
      *
      * @return the set of Permissions according to the policy.
@@ -1157,8 +1151,8 @@
      * object with additional permissions granted to the specified
      * CodeSource.
      *
-     * @param permissions the permissions to populate
-     * @param codesource the codesource associated with the caller.
+     * @param perms the permissions to populate
+     * @param cs the codesource associated with the caller.
      * This encapsulates the original location of the code (where the code
      * came from) and the public key(s) of its signer.
      *
@@ -1386,7 +1380,7 @@
                         accPs,
                         perms);
             } else {
-                perms.add(p);
+                perms.add(FilePermCompat.newPermPlusAltPath(p));
             }
         }
     }
@@ -1458,9 +1452,9 @@
         }
         try {
             // first try to instantiate the permission
-            perms.add(getInstance(sp.getSelfType(),
+            perms.add(FilePermCompat.newPermPlusAltPath(getInstance(sp.getSelfType(),
                                   sb.toString(),
-                                  sp.getSelfActions()));
+                                  sp.getSelfActions())));
         } catch (ClassNotFoundException cnfe) {
             // ok, the permission is not in the bootclasspath.
             // before we add an UnresolvedPermission, check to see
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/FilePermCompat.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+import sun.security.action.GetPropertyAction;
+
+import java.io.FilePermission;
+import java.security.Permission;
+import jdk.internal.misc.SharedSecrets;
+
+/**
+ * Take care of FilePermission compatibility after JDK-8164705.
+ */
+public class FilePermCompat {
+    /**
+     * New behavior? Keep compatibility? Both default true.
+     */
+    public static final boolean nb;
+    public static final boolean compat;
+
+    static {
+        String flag = GetPropertyAction.privilegedGetProperty(
+                "jdk.io.permissionsUseCanonicalPath", "false");
+        switch (flag) {
+            case "true":
+                nb = false;
+                compat = false;
+                break;
+            case "false":
+                nb = true;
+                compat = true;
+                break;
+            default:
+                throw new RuntimeException(
+                        "Invalid jdk.io.permissionsUseCanonicalPath: " + flag);
+        }
+    }
+
+    public static Permission newPermPlusAltPath(Permission input) {
+        if (compat && input instanceof FilePermission) {
+            return SharedSecrets.getJavaIOFilePermissionAccess()
+                    .newPermPlusAltPath((FilePermission) input);
+        }
+        return input;
+    }
+
+    public static Permission newPermUsingAltPath(Permission input) {
+        if (input instanceof FilePermission) {
+            return SharedSecrets.getJavaIOFilePermissionAccess()
+                    .newPermUsingAltPath((FilePermission) input);
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/Correctness.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ *
+ * @test
+ * @bug 8164705
+ * @summary Remove pathname canonicalization from FilePermission
+ */
+
+import java.io.FilePermission;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class Correctness {
+
+    static boolean err = false;
+    static Method containsMethod;
+    static boolean isWindows =
+            System.getProperty("os.name").contains("Windows");
+    public static void main(String args[]) throws Exception {
+        check("/", "/");
+        checkNo("/", "/x");
+        checkNo("/", "/../x");
+
+        checkNo("/", "x");
+
+        check("/-", "/*");
+        checkNo("/*", "/-");
+
+        check("/*", "/x");
+        check("/-", "/x");
+        check("/-", "/x/*");
+        check("/-", "/x/-");
+        check("/-", "/x/y");
+        checkNo("/*", "/x/y");
+        check("/x/*", "/x/x");
+        checkNo("/x/-", "/x");
+        checkNo("/x/*", "/x");
+        check("/x/-", "/x/x");
+        check("/x/-", "/x/x/y");
+        checkNo("/x/*", "/x/x/y");
+        checkNo("/x/*", "/x");
+
+        check("*", "x");
+        checkNo("", "x");
+        check("-", "x");
+        check("-", "*");
+        check("-", "a/-");
+        check("-", "a/*");
+        checkNo("*", "a/b");
+        check("a/*", "a/b");
+        check("a/-", "a/*");
+        check("a/-", "a/b/c");
+        checkNo("a/*", "a/b/c");
+
+        check("../", "../");
+        check("../-", "../*");
+        check("../../*", "../../a");
+
+        // If we allow .. and abs/rel checks
+        check("../-", "a");
+        check("../../-", "-");
+        checkNo("../../*", "a");
+        //check("/-", "a");
+        //checkNo("/*", "a");
+        //check("/-", "-");
+
+        try {
+            // containsPath is broken on Windows.
+            containsMethod = FilePermission.class.getDeclaredMethod(
+                    "containsPath", Path.class, Path.class);
+            containsMethod.setAccessible(true);
+            System.out.println();
+
+            contains("x", "x", 0);
+            contains("x", "x/y", 1);
+            contains("x", "x/y/z", 2);
+            contains("x", "y", -1);
+            contains("x", "", -1);
+            contains("", "", 0);
+            contains("", "x", 1);
+            contains("", "x/y", 2);
+            contains("/", "/", 0);
+            contains("/", "/x", 1);
+            contains("/", "/x/y", 2);
+            contains("/x", "/x/y", 1);
+            contains("/x", "/y", -1);
+            //contains("/", "..", Integer.MAX_VALUE);
+            //contains("/", "x", Integer.MAX_VALUE);
+            //contains("/", "x/y", Integer.MAX_VALUE);
+            //contains("/", "../x", Integer.MAX_VALUE);
+            contains("/x", "y", -1);
+            contains("x", "/y", -1);
+
+            contains("", "..", -1);
+            contains("", "../x", -1);
+            contains("..", "", 1);
+            contains("..", "x", 2);
+            contains("..", "x/y", 3);
+            contains("../x", "x", -1);
+            contains("../x", "y", -1);
+            contains("../x", "../x/y", 1);
+            contains("../../x", "../../x/y", 1);
+            contains("../../../x", "../../../x/y", 1);
+            contains("../x", "../y", -1);
+        } catch (NoSuchMethodException e) {
+            // Ignored
+        }
+        if (err) throw new Exception("Failed.");
+    }
+
+    // Checks if s2 is inside s1 and depth is expected.
+    static void contains(String s1, String s2, int expected) throws Exception {
+        contains0(s1, s2, expected);
+        if (isWindows) {
+            contains0("C:" + s1, s2, -1);
+            contains0(s1, "C:" + s2, -1);
+            contains0("C:" + s1, "D:" + s2, -1);
+            contains0("C:" + s1, "C:" + s2, expected);
+        }
+    }
+
+    static void contains0(String s1, String s2, int expected) throws Exception {
+        Path p1 = Paths.get(s1);
+        Path p2 = Paths.get(s2);
+        int d = (int)containsMethod.invoke(null, p1, p2);
+        Path p;
+        try {
+            p = p2.relativize(p1);
+        } catch (Exception e) {
+            p = null;
+        }
+        System.out.printf("%-20s -> %-20s: %20s %5d %5d %s\n", s1, s2, p,
+                d, expected, d==expected?"":" WRONG");
+        if (d != expected) {
+            err = true;
+        }
+    }
+
+    static void check(String s1, String s2, boolean expected) {
+        FilePermission fp1 = new FilePermission(s1, "read");
+        FilePermission fp2 = new FilePermission(s2, "read");
+        boolean b = fp1.implies(fp2);
+        System.out.printf("%-30s -> %-30s: %5b %s\n",
+                s1, s2, b, b==expected?"":" WRONG");
+        if (b != expected) {
+            err = true;
+            System.out.println(fp1);
+            System.out.println(fp2);
+        }
+    }
+
+    static void check(String s1, String s2) {
+        check(s1, s2, true);
+    }
+
+    static void checkNo(String s1, String s2) {
+        check(s1, s2, false);
+    }
+}
--- a/jdk/test/java/io/FilePermission/FilePermissionCollection.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/io/FilePermission/FilePermissionCollection.java	Mon Oct 10 08:28:50 2016 +0800
@@ -47,14 +47,14 @@
             ("test 1: add throws IllegalArgExc for wrong perm type");
         try {
             perms.add(new SecurityPermission("createAccessControlContext"));
-            System.err.println("Expected IllegalArgumentException");
+            System.out.println("Expected IllegalArgumentException");
             testFail++;
         } catch (IllegalArgumentException iae) {}
 
         // test 2
         System.out.println("test 2: implies returns false for wrong perm type");
         if (perms.implies(new SecurityPermission("getPolicy"))) {
-            System.err.println("Expected false, returned true");
+            System.out.println("Expected false, returned true");
             testFail++;
         }
 
@@ -63,7 +63,7 @@
                            "name and action");
         perms.add(new FilePermission("/tmp/foo", "read"));
         if (!perms.implies(new FilePermission("/tmp/foo", "read"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
 
@@ -71,7 +71,7 @@
         System.out.println("test 4: implies returns false for match on " +
                            "name but not action");
         if (perms.implies(new FilePermission("/tmp/foo", "write"))) {
-            System.err.println("Expected false, returned true");
+            System.out.println("Expected false, returned true");
             testFail++;
         }
 
@@ -80,7 +80,7 @@
                            "name and subset of actions");
         perms.add(new FilePermission("/tmp/bar", "read, write"));
         if (!perms.implies(new FilePermission("/tmp/bar", "write"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
 
@@ -90,11 +90,11 @@
         perms.add(new FilePermission("/tmp/baz", "read"));
         perms.add(new FilePermission("/tmp/baz", "write"));
         if (!perms.implies(new FilePermission("/tmp/baz", "read"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
         if (!perms.implies(new FilePermission("/tmp/baz", "write,read"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
 
@@ -103,7 +103,7 @@
                            "and match on action");
         perms.add(new FilePermission("/usr/tmp/*", "read"));
         if (!perms.implies(new FilePermission("/usr/tmp/foo", "read"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
 
@@ -111,7 +111,7 @@
         System.out.println
             ("test 8: implies returns false for non-match on wildcard");
         if (perms.implies(new FilePermission("/usr/tmp/bar/foo", "read"))) {
-            System.err.println("Expected false, returned true");
+            System.out.println("Expected false, returned true");
             testFail++;
         }
 
@@ -120,25 +120,25 @@
             ("test 9: implies returns true for deep wildcard match");
         perms.add(new FilePermission("/usr/tmp/-", "read"));
         if (!perms.implies(new FilePermission("/usr/tmp/bar/foo", "read"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
 
         // test 10
-        System.out.println("test 10: implies returns true for relative match");
+        //System.out.println("test 10: implies returns true for relative match");
         perms.add(new FilePermission(".", "read"));
-        if (!perms.implies(new FilePermission(System.getProperty("user.dir"),
-                                              "read"))) {
-            System.err.println("Expected true, returned false");
-            testFail++;
-        }
+        //if (!perms.implies(new FilePermission(System.getProperty("user.dir"),
+        //                                      "read"))) {
+        //    System.out.println("Expected true, returned false");
+        //    testFail++;
+        //}
 
         // test 11
         System.out.println("test 11: implies returns true for all " +
                            "wildcard and match on action");
         perms.add(new FilePermission("<<ALL FILES>>", "read"));
         if (!perms.implies(new FilePermission("/tmp/foobar", "read"))) {
-            System.err.println("Expected true, returned false");
+            System.out.println("Expected true, returned false");
             testFail++;
         }
 
@@ -146,7 +146,7 @@
         System.out.println("test 12: implies returns false for wildcard " +
                            "and non-match on action");
         if (perms.implies(new FilePermission("/tmp/foobar", "write"))) {
-            System.err.println("Expected false, returned true");
+            System.out.println("Expected false, returned true");
             testFail++;
         }
 
@@ -160,7 +160,7 @@
         }
         // the two "/tmp/baz" entries were combined into one
         if (numPerms != 7) {
-            System.err.println("Expected 7, got " + numPerms);
+            System.out.println("Expected 7, got " + numPerms);
             testFail++;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/ReadFileOnPath.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164705
+ * @library /lib/testlibrary /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @run main ReadFileOnPath
+ * @summary Still able to read file on the same path
+ */
+
+import jdk.test.lib.process.ProcessTools;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ReadFileOnPath {
+
+    private static final Path SRC_DIR    = Paths.get(System.getProperty("test.src"));
+    private static final Path HERE_DIR   = Paths.get(".");
+    private static final Path MODS_DIR   = Paths.get("modules");
+
+    public static void main(String args[]) throws Exception {
+        CompilerUtils.compile(SRC_DIR.resolve("m"), MODS_DIR.resolve("m"));
+        Files.write(MODS_DIR.resolve("m/base"), "base".getBytes());
+        Files.write(MODS_DIR.resolve("m/p/child"), "child".getBytes());
+        JarUtils.createJarFile(HERE_DIR.resolve("old.jar"),
+                MODS_DIR.resolve("m"),
+                "base", "p/App.class", "p/child");
+        JarUtils.createJarFile(HERE_DIR.resolve("new.jar"),
+                MODS_DIR.resolve("m"),
+                "module-info.class", "base", "p/App.class", "p/child");
+
+        // exploded module
+        test("--module-path", "modules", "-m", "m/p.App", "SS+++++");
+
+        // module in jar
+        test("--module-path", "new.jar", "-m", "m/p.App", "SSSS++0");
+
+        // exploded classpath
+        test("-cp", "modules/m", "p.App", "SS+++++");
+
+        // classpath in jar
+        test("-cp", "old.jar", "p.App", "SSSS++0");
+    }
+
+    static void test(String... args) throws Exception {
+        List<String> cmds = new ArrayList<>();
+        cmds.add("-Djava.security.manager");
+        cmds.addAll(Arrays.asList(args));
+        cmds.addAll(List.of(
+                "x", "modules/m", "modules/m/base", "modules/m/p/child",
+                "-", "child", "/base", "../base"));
+        ProcessTools.executeTestJvm(cmds.toArray(new String[cmds.size()]))
+                .shouldHaveExitValue(0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/m/module-info.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,3 @@
+module m {
+    exports p;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/m/p/App.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,44 @@
+package p;
+import java.io.InputStream;
+import java.io.FileInputStream;
+public class App {
+    public static void main(String[] args) throws Exception {
+        boolean f = true;
+        StringBuilder sb = new StringBuilder();
+        String expected = null;
+        for (String s: args) {
+            if (expected == null) {
+                expected = s;
+            } else if (s.equals("-")) {
+                f = false;
+            } else if (f) {
+                try (InputStream is = new FileInputStream(s)) {
+                    is.readAllBytes();
+                    sb.append('+');
+                } catch (SecurityException se) {
+                    System.out.println(se);
+                    sb.append('S');
+                } catch (Exception e) {
+                    System.out.println(e);
+                    sb.append('-');
+                }
+            } else {
+                try (InputStream is = App.class.getResourceAsStream(s)) {
+                    is.readAllBytes();
+                    sb.append('+');
+                } catch (NullPointerException npe) {
+                    System.out.println(npe);
+                    sb.append('0');
+                } catch (Exception e) {
+                    System.out.println(e);
+                    sb.append('-');
+                }
+            }
+        }
+        if (!sb.toString().equals(expected)) {
+            throw new Exception("Expected " + expected + ", actually " + sb);
+        } else {
+            System.out.println("OK");
+        }
+    }
+}
--- a/jdk/test/java/net/URLClassLoader/getresourceasstream/Test.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/net/URLClassLoader/getresourceasstream/Test.java	Mon Oct 10 08:28:50 2016 +0800
@@ -26,12 +26,12 @@
 
 public class Test {
     public static void main (String[] args) throws Exception {
-        test1();
+        test1(args[0]);
     }
 
-    public static void test1 () throws Exception {
+    public static void test1 (String s) throws Exception {
         URLClassLoader cl = new URLClassLoader (new URL[] {
-            new URL ("file:./test.jar")
+            new URL ("file:" + s)
         });
         Class clazz = Class.forName ("Test\u00a3", true, cl);
         InputStream is = clazz.getResourceAsStream ("Test\u00a3.class");
--- a/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh	Mon Oct 10 08:28:50 2016 +0800
@@ -39,18 +39,33 @@
 
 checkExit () {
     if [ $? != 0 ]; then
-	exit 1;
+	exit $1;
     fi
 }
 
 ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Test.java
 cp ${TESTSRC}/test.jar .
 
-${TESTJAVA}/bin/java ${TESTVMOPTS} Test
-checkExit 
+${TESTJAVA}/bin/java ${TESTVMOPTS} Test ./test.jar
+checkExit 1
 
 # try with security manager
 
-${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:./policy -Djava.security.manager Test
-checkExit 
+${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:./policy \
+		-Djava.security.manager Test ./test.jar
+checkExit 2
+
+mkdir tmp
+cd tmp
+${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:../policy \
+		-cp .. -Djava.security.manager Test ../test.jar
+checkExit 3
+
+cd ..
+THISDIR=$(basename $(pwd))
+cd ..
+${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:$THISDIR/policy \
+		-cp $THISDIR -Djava.security.manager Test $THISDIR/test.jar
+checkExit 4
+
 exit 0
--- a/jdk/test/java/security/testlibrary/Proc.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/security/testlibrary/Proc.java	Mon Oct 10 08:28:50 2016 +0800
@@ -29,6 +29,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.Permission;
+import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Base64;
@@ -37,6 +38,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 /**
@@ -49,7 +51,8 @@
  *        .args("x")            // with args
  *        .env("env", "value")  // and an environment variable
  *        .prop("key","value")  // and a system property
- *        .perm(perm)           // with granted permissions
+ *        .grant(file)          // grant codes in this codebase
+ *        .perm(perm)           // with the permission
  *        .start();             // and start
  *
  * create/start must be called, args/env/prop/perm can be called zero or
@@ -57,7 +60,7 @@
  *
  * The controller can call inheritIO to share its I/O to the process.
  * Otherwise, it can send data into a proc's stdin with write/println, and
- * read its stdout with readLine. stderr is always redirected to DFILE
+ * read its stdout with readLine. stderr is always redirected to a file
  * unless nodump() is called. A protocol is designed to make
  * data exchange among the controller and the processes super easy, in which
  * useful data are always printed with a special prefix ("PROCISFUN:").
@@ -84,10 +87,10 @@
  *
  * As the Proc objects are hidden so deeply, two static methods, d(String) and
  * d(Throwable) are provided to output info into stderr, where they will
- * normally be appended messages to DFILE (unless nodump() is called).
+ * normally be appended messages to a debug file (unless nodump() is called).
  * Developers can view the messages in real time by calling
  *
- *    tail -f proc.debug
+ *    {@code tail -f stderr.<debug>}
  *
  * TODO:
  *
@@ -104,19 +107,24 @@
     private BufferedReader br;      // the stdout of a process
     private String launcher;        // Optional: the java program
 
-    private List<Permission> perms = new ArrayList<>();
     private List<String> args = new ArrayList<>();
     private Map<String,String> env = new HashMap<>();
     private Map<String,String> prop = new HashMap();
     private boolean inheritIO = false;
     private boolean noDump = false;
 
+    private List<String> cp;        // user-provided classpath
     private String clazz;           // Class to launch
     private String debug;           // debug flag, controller will show data
-                                    // transfer between procs
+                                    // transfer between procs. If debug is set,
+                                    // it MUST be different between Procs.
 
     final private static String PREFIX = "PROCISFUN:";
-    final private static String DFILE = "proc.debug";
+
+    // policy file
+    final private StringBuilder perms = new StringBuilder();
+    // temporary saving the grant line in a policy file
+    final private StringBuilder grant = new StringBuilder();
 
     // The following methods are called by controllers
 
@@ -168,10 +176,68 @@
         prop.put(a, b);
         return this;
     }
-    // Adds a perm to policy. Can be called multiple times. In order to make it
-    // effective, please also call prop("java.security.manager", "").
+    // Sets classpath. If not called, Proc will choose a classpath. If called
+    // with no arg, no classpath will be used. Can be called multiple times.
+    public Proc cp(String... s) {
+        if (cp == null) {
+            cp = new ArrayList<>();
+        }
+        cp.addAll(Arrays.asList(s));
+        return this;
+    }
+    // Adds a permission to policy. Can be called multiple times.
+    // All perm() calls after a series of grant() calls are grouped into
+    // a single grant block. perm() calls before any grant() call are grouped
+    // into a grant block with no restriction.
+    // Please note that in order to make permissions effective, also call
+    // prop("java.security.manager", "").
     public Proc perm(Permission p) {
-        perms.add(p);
+        if (grant.length() != 0) {      // Right after grant(s)
+            if (perms.length() != 0) {  // Not first block
+                perms.append("};\n");
+            }
+            perms.append("grant ").append(grant).append(" {\n");
+            grant.setLength(0);
+        } else {
+            if (perms.length() == 0) {  // First block w/o restriction
+                perms.append("grant {\n");
+            }
+        }
+        if (p.getActions().isEmpty()) {
+            String s = String.format("%s \"%s\"",
+                    p.getClass().getCanonicalName(),
+                    p.getName()
+                            .replace("\\", "\\\\").replace("\"", "\\\""));
+            perms.append("    permission ").append(s).append(";\n");
+        } else {
+            String s = String.format("%s \"%s\", \"%s\"",
+                    p.getClass().getCanonicalName(),
+                    p.getName()
+                            .replace("\\", "\\\\").replace("\"", "\\\""),
+                    p.getActions());
+            perms.append("    permission ").append(s).append(";\n");
+        }
+        return this;
+    }
+
+    // Adds a grant option to policy. If called in a row, a single grant block
+    // with all options will be created. If there are perm() call(s) between
+    // grant() calls, they belong to different grant blocks
+
+    // grant on a principal
+    public Proc grant(Principal p) {
+        grant.append("principal ").append(p.getClass().getName())
+                .append(" \"").append(p.getName()).append("\", ");
+        return this;
+    }
+    // grant on a codebase
+    public Proc grant(File f) {
+        grant.append("codebase \"").append(f.toURI()).append("\", ");
+        return this;
+    }
+    // arbitrary grant
+    public Proc grant(String v) {
+        grant.append(v).append(", ");
         return this;
     }
     // Starts the proc
@@ -191,30 +257,22 @@
         Collections.addAll(cmd, splitProperty("test.vm.opts"));
         Collections.addAll(cmd, splitProperty("test.java.opts"));
 
-        cmd.add("-cp");
-        cmd.add(System.getProperty("test.class.path") + File.pathSeparator +
-                System.getProperty("test.src.path"));
+        if (cp == null) {
+            cmd.add("-cp");
+            cmd.add(System.getProperty("test.class.path") + File.pathSeparator +
+                    System.getProperty("test.src.path"));
+        } else if (!cp.isEmpty()) {
+            cmd.add("-cp");
+            cmd.add(cp.stream().collect(Collectors.joining(File.pathSeparator)));
+        }
 
         for (Entry<String,String> e: prop.entrySet()) {
             cmd.add("-D" + e.getKey() + "=" + e.getValue());
         }
-        if (!perms.isEmpty()) {
-            Path p = Files.createTempFile(
-                    Paths.get(".").toAbsolutePath(), "policy", null);
-            StringBuilder sb = new StringBuilder();
-            sb.append("grant {\n");
-            for (Permission perm: perms) {
-                // Sometimes a permission has no name or actions.
-                // but it's safe to use an empty string.
-                String s = String.format("%s \"%s\", \"%s\"",
-                        perm.getClass().getCanonicalName(),
-                        perm.getName()
-                                .replace("\\", "\\\\").replace("\"", "\\\""),
-                        perm.getActions());
-                sb.append("    permission ").append(s).append(";\n");
-            }
-            sb.append("};\n");
-            Files.write(p, sb.toString().getBytes());
+        if (perms.length() > 0) {
+            Path p = Paths.get(getId("policy")).toAbsolutePath();
+            perms.append("};\n");
+            Files.write(p, perms.toString().getBytes());
             cmd.add("-Djava.security.policy=" + p.toString());
         }
         cmd.add(clazz);
@@ -223,6 +281,15 @@
         }
         if (debug != null) {
             System.out.println("PROC: " + debug + " cmdline: " + cmd);
+            for (String c : cmd) {
+                if (c.indexOf('\\') >= 0 || c.indexOf(' ') > 0) {
+                    System.out.print('\'' + c + '\'');
+                } else {
+                    System.out.print(c);
+                }
+                System.out.print(' ');
+            }
+            System.out.println();
         }
         ProcessBuilder pb = new ProcessBuilder(cmd);
         for (Entry<String,String> e: env.entrySet()) {
@@ -233,12 +300,17 @@
         } else if (noDump) {
             pb.redirectError(ProcessBuilder.Redirect.INHERIT);
         } else {
-            pb.redirectError(ProcessBuilder.Redirect.appendTo(new File(DFILE)));
+            pb.redirectError(ProcessBuilder.Redirect
+                    .appendTo(new File(getId("stderr"))));
         }
         p = pb.start();
         br = new BufferedReader(new InputStreamReader(p.getInputStream()));
         return this;
     }
+    String getId(String prefix) {
+        if (debug != null) return prefix + "." + debug;
+        else return prefix + "." + System.identityHashCode(this);
+    }
     // Reads a line from stdout of proc
     public String readLine() throws IOException {
         String s = br.readLine();
--- a/jdk/test/sun/security/provider/PolicyFile/CanonPath.java	Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/sun/security/provider/PolicyFile/CanonPath.java	Mon Oct 10 08:28:50 2016 +0800
@@ -150,9 +150,9 @@
             //
             // on unix, /- implies everything
 
-            if (w.implies(u) || !u.implies(w)) {
-                throw new Exception("SLASH/- test failed");
-            }
+            //if (w.implies(u) || !u.implies(w)) {
+            //    throw new Exception("SLASH/- test failed");
+            //}
         }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/FilePermCompat/CompatImpact.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164705
+ * @summary check compatibility after FilePermission change
+ * @library /java/security/testlibrary/
+ * @modules java.base/jdk.internal.misc
+ * @run main CompatImpact prepare
+ * @run main CompatImpact builtin
+ * @run main CompatImpact mine
+ * @run main CompatImpact dopriv
+ */
+
+import java.io.File;
+import java.io.FilePermission;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+
+public class CompatImpact {
+
+    public static void main(String[] args) throws Exception {
+        switch (args[0]) {
+            // copy class files to future classpath
+            case "prepare":
+                // cp in .
+                String cp = System.getProperty("test.classes");
+                Files.copy(Paths.get(cp, "CompatImpact.class"),
+                        Paths.get("CompatImpact.class"));
+                Files.copy(Paths.get(cp, "CompatImpact$MP.class"),
+                        Paths.get("CompatImpact$MP.class"));
+                Files.write(Paths.get("f"), new byte[10]);
+                // cp in ./sub
+                Files.createDirectory(Paths.get("sub"));
+                Files.copy(Paths.get(cp, "CompatImpact.class"),
+                        Paths.get("sub", "CompatImpact.class"));
+                Files.copy(Paths.get(cp, "CompatImpact$MP.class"),
+                        Paths.get("sub", "CompatImpact$MP.class"));
+                Files.write(Paths.get("sub", "f"), new byte[10]);
+                // cp in ./inner
+                Files.createDirectory(Paths.get("inner"));
+                Files.copy(Paths.get(cp, "CompatImpact$DoPrivInner.class"),
+                        Paths.get("inner", "CompatImpact$DoPrivInner.class"));
+                break;
+            // run tests with different policy impls
+            case "builtin":
+            case "mine":
+                cp = System.getProperty("test.classes");
+                Proc p;
+                String failed = "";
+                String testcase = "";
+                String cwd = System.getProperty("user.dir");
+
+                // Granting a FilePermission on an absolute path
+                testcase = "PonA";
+                p = p(args[0], cwd + "/f")
+                        .args("f", cwd + "/f")
+                        .debug(testcase)
+                        .start();
+                if (p.waitFor() != 0) {
+                    Files.copy(Paths.get("stderr." + testcase), System.out);
+                    failed += testcase + " ";
+                }
+
+                // Granting a FilePermission on a relative path
+                testcase = "PonR";
+                p = p(args[0], "f")
+                        .args("f", cwd + "/f")
+                        .debug(testcase)
+                        .start();
+                if (p.waitFor() != 0) {
+                    Files.copy(Paths.get("stderr." + testcase), System.out);
+                    failed += testcase + " ";
+                }
+
+                // Reading file on classpath, not cwd
+                testcase = "cp";
+                String cprel = Paths.get(cwd).relativize(Paths.get(cp))
+                        .normalize().toString();
+                p = p(args[0], "x")
+                        .args(cp + "/f", cprel + "/f")
+                        .debug(testcase)
+                        .start();
+                if (p.waitFor() != 0) {
+                    Files.copy(Paths.get("stderr." + testcase), System.out);
+                    failed += testcase + " ";
+                }
+
+                // Reading file on classpath, cwd
+                testcase = "cpHere";
+                p = p(args[0], "x")
+                        .args(cwd + "/f", "f", "RES")
+                        .cp(".")   // Must! cancel the old CLASSPATH.
+                        .debug(testcase)
+                        .start();
+                if (p.waitFor() != 0) {
+                    Files.copy(Paths.get("stderr." + testcase), System.out);
+                    failed += testcase + " ";
+                }
+
+                // Reading file on classpath, cwd
+                testcase = "cpSub";
+                p = p(args[0], "x")
+                        .args(cwd + "/sub/f", "sub/f", "RES")
+                        .cp("sub")   // Must! There's CLASSPATH.
+                        .debug(testcase)
+                        .start();
+                if (p.waitFor() != 0) {
+                    Files.copy(Paths.get("stderr." + testcase), System.out);
+                    failed += testcase + " ";
+                }
+
+                if (!failed.isEmpty()) {
+                    throw new Exception(failed + "failed");
+                }
+                break;
+            // test <policy_type> <grant> <read...>
+            case "test":
+                if (args[1].equals("mine")) {
+                    Policy.setPolicy(new MP(args[2]));
+                }
+                Exception e = null;
+                for (int i = 3; i < args.length; i++) {
+                    try {
+                        System.out.println(args[i]);
+                        if (args[i].equals("RES")) {
+                            CompatImpact.class.getResourceAsStream("f")
+                                    .close();
+                        } else {
+                            new File(args[i]).exists();
+                        }
+                    } catch (Exception e2) {
+                        e = e2;
+                        e2.printStackTrace(System.out);
+                    }
+                }
+                if (e != null) {
+                    System.err.println("====================");
+                    throw e;
+                }
+                break;
+            // doPrivWithPerm test launcher
+            case "dopriv":
+                cwd = System.getProperty("user.dir");
+                // caller (CompatImpact doprivouter, no permission) in sub,
+                // executor (DoPrivInner, AllPermission) in inner.
+                p = Proc.create("CompatImpact")
+                        .args("doprivouter")
+                        .prop("java.security.manager", "")
+                        .grant(new File("inner"))
+                        .perm(new AllPermission())
+                        .cp("sub", "inner")
+                        .debug("doPriv")
+                        .args(cwd)
+                        .start();
+                if (p.waitFor() != 0) {
+                    throw new Exception("dopriv test fails");
+                }
+                break;
+            // doprivouter <cwd>
+            case "doprivouter":
+                DoPrivInner.main(args);
+                break;
+            default:
+                throw new Exception("unknown " + args[0]);
+        }
+    }
+
+    // Call by CompatImpact doprivouter, with AllPermission
+    public static class DoPrivInner {
+        public static void main(String[] args) throws Exception {
+            AccessController.doPrivileged((PrivilegedAction<Boolean>)
+                            () -> new File("x").exists(),
+                    null,
+                    new FilePermission(args[1] + "/x", "read"));
+            AccessController.doPrivileged((PrivilegedAction<Boolean>)
+                            () -> new File(args[1] + "/x").exists(),
+                    null,
+                    new FilePermission("x", "read"));
+            try {
+                AccessController.doPrivileged((PrivilegedAction<Boolean>)
+                                () -> new File("x").exists(),
+                        null,
+                        new FilePermission("y", "read"));
+                throw new Exception("Should not read");
+            } catch (SecurityException se) {
+                // Expected
+            }
+        }
+    }
+
+    // Return a Proc object for different policy types
+    private static Proc p(String type, String f) throws Exception {
+        Proc p = Proc.create("CompatImpact")
+                .prop("java.security.manager", "");
+        p.args("test", type);
+        switch (type) {
+            case "builtin":
+                // For builtin policy, reading access to f can be
+                // granted as a permission
+                p.perm(new FilePermission(f, "read"));
+                p.args("-");
+                break;
+            case "mine":
+                // For my policy, f is passed into test and new MP(f)
+                // will be set as new policy
+                p.perm(new SecurityPermission("setPolicy"));
+                p.args(f);
+                break;
+            default:
+                throw new Exception("unknown " + type);
+        }
+        return p;
+    }
+
+    // My own Policy impl, with only one granted permission, also not smart
+    // enough to know whether ProtectionDomain grants any permission
+    static class MP extends Policy {
+        final PermissionCollection pc;
+        MP(String f) {
+            FilePermission p = new FilePermission(f, "read");
+            pc = p.newPermissionCollection();
+            pc.add(p);
+        }
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return pc;
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return pc;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return pc.implies(permission);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/FilePermCompat/Flag.java	Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164705
+ * @summary check jdk.filepermission.canonicalize
+ * @library /java/security/testlibrary/
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm -Djdk.io.permissionsUseCanonicalPath=true Flag truetrue
+ * @run main/othervm -Djdk.io.permissionsUseCanonicalPath=false Flag falsetrue
+ * @run main/othervm Flag falsetrue
+ */
+
+import java.io.File;
+import java.io.FilePermission;
+import java.lang.*;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+
+public class Flag {
+    public static void main(String[] args) throws Exception {
+
+        boolean test1;
+        boolean test2;
+
+        String here = System.getProperty("user.dir");
+        File abs = new File(here, "x");
+        FilePermission fp1 = new FilePermission("x", "read");
+        FilePermission fp2 = new FilePermission(abs.toString(), "read");
+        test1 = fp1.equals(fp2);
+
+        Policy pol = new Policy() {
+            @java.lang.Override
+            public boolean implies(ProtectionDomain domain, Permission permission) {
+                return fp1.implies(permission);
+            }
+        };
+
+        Policy.setPolicy(pol);
+        System.setSecurityManager(new SecurityManager());
+        try {
+            System.getSecurityManager().checkPermission(fp2);
+            test2 = true;
+        } catch (SecurityException se) {
+            test2 = false;
+        }
+
+        if (!args[0].equals(test1 + "" + test2)) {
+            throw new Exception("Test failed: " + test1 + test2);
+        }
+    }
+}