6870926: (file) Path.toRealPath performance can be improved (win)
authoralanb
Thu, 20 Aug 2009 08:42:38 +0100
changeset 3628 2768d95a0e7d
parent 3627 d0ad40d5adab
child 3629 6035eed86b12
6870926: (file) Path.toRealPath performance can be improved (win) Reviewed-by: sherman
jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java
jdk/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java
jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java
jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java	Thu Aug 20 08:39:18 2009 +0100
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java	Thu Aug 20 08:42:38 2009 +0100
@@ -246,8 +246,8 @@
         long lastWriteTime = unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME);
         long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
             + (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
-        int reparseTag = ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) ?
-            + unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
+        int reparseTag = isReparsePoint(fileAttrs) ?
+            unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
         return new WindowsFileAttributes(fileAttrs,
                                          creationTime,
                                          lastAccessTime,
@@ -275,7 +275,7 @@
             int reparseTag = 0;
             int fileAttrs = unsafe
                 .getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES);
-            if ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+            if (isReparsePoint(fileAttrs)) {
                 int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
                 NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size);
                 try {
@@ -311,7 +311,7 @@
                 // just return the attributes
                 int fileAttrs = unsafe
                     .getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES);
-                if ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+                if (!isReparsePoint(fileAttrs))
                     return fromFileAttributeData(address, 0);
             } catch (WindowsException x) {
                 if (x.lastError() != ERROR_SHARING_VIOLATION)
@@ -358,7 +358,7 @@
     }
 
     /**
-     * Returns true if the attribtues are of the same file - both files must
+     * Returns true if the attributes are of the same file - both files must
      * be open.
      */
     static boolean isSameFile(WindowsFileAttributes attrs1,
@@ -370,6 +370,13 @@
                (attrs1.fileIndexLow == attrs2.fileIndexLow);
     }
 
+    /**
+     * Returns true if the attributes are of a file with a reparse point.
+     */
+    static boolean isReparsePoint(int attributes) {
+        return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+    }
+
     // package-private
     int attributes() {
         return fileAttrs;
@@ -420,7 +427,7 @@
 
     // package private
     boolean isReparsePoint() {
-        return (fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+        return isReparsePoint(fileAttrs);
     }
 
     boolean isDirectoryLink() {
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java	Thu Aug 20 08:39:18 2009 +0100
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java	Thu Aug 20 08:42:38 2009 +0100
@@ -63,6 +63,30 @@
     }
 
     /**
+     * Returns the final path (all symbolic links resolved) or null if this
+     * operation is not supported.
+     */
+    private static String getFinalPath(WindowsPath input) throws IOException {
+        long h = 0;
+        try {
+            h = input.openForReadAttributeAccess(true);
+        } catch (WindowsException x) {
+            x.rethrowAsIOException(input);
+        }
+        try {
+            return stripPrefix(GetFinalPathNameByHandle(h));
+        } catch (WindowsException x) {
+            // ERROR_INVALID_LEVEL is the error returned when not supported
+            // (a sym link to file on FAT32 or Samba server for example)
+            if (x.lastError() != ERROR_INVALID_LEVEL)
+                x.rethrowAsIOException(input);
+        } finally {
+            CloseHandle(h);
+        }
+        return null;
+    }
+
+    /**
      * Returns the final path of a given path as a String. This should be used
      * prior to calling Win32 system calls that do not follow links.
      */
@@ -70,7 +94,6 @@
         throws IOException
     {
         WindowsFileSystem fs = input.getFileSystem();
-
         try {
             // if not following links then don't need final path
             if (!followLinks || !fs.supportsLinks())
@@ -84,25 +107,10 @@
             x.rethrowAsIOException(input);
         }
 
-        // The file is a symbolic link so we open it and try to get the
-        // normalized path. This should succeed on NTFS but may fail if there
-        // is a link to a non-NFTS file system.
-        long h = 0;
-        try {
-            h = input.openForReadAttributeAccess(true);
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(input);
-        }
-        try {
-            return stripPrefix(GetFinalPathNameByHandle(h));
-        } catch (WindowsException x) {
-            // ERROR_INVALID_LEVEL is the error returned when not supported by
-            // the file system
-            if (x.lastError() != ERROR_INVALID_LEVEL)
-                x.rethrowAsIOException(input);
-        } finally {
-            CloseHandle(h);
-        }
+        // The file is a symbolic link so attempt to get the final path
+        String result = getFinalPath(input);
+        if (result != null)
+            return result;
 
         // Fallback: read target of link, resolve against parent, and repeat
         // until file is not a link.
@@ -149,31 +157,9 @@
         throws IOException
     {
         WindowsFileSystem fs = input.getFileSystem();
-        if (!fs.supportsLinks())
+        if (resolveLinks && !fs.supportsLinks())
             resolveLinks = false;
 
-        // On Vista use GetFinalPathNameByHandle. This should succeed on NTFS
-        // but may fail if there is a link to a non-NFTS file system.
-        if (resolveLinks) {
-            long h = 0;
-            try {
-                h = input.openForReadAttributeAccess(true);
-            } catch (WindowsException x) {
-                x.rethrowAsIOException(input);
-            }
-            try {
-                return stripPrefix(GetFinalPathNameByHandle(h));
-            } catch (WindowsException x) {
-                if (x.lastError() != ERROR_INVALID_LEVEL)
-                    x.rethrowAsIOException(input);
-            } finally {
-                CloseHandle(h);
-            }
-        }
-
-        // Not resolving links or we are on Windows Vista (or newer) with a
-        // link to non-NFTS file system.
-
         // Start with absolute path
         String path = null;
         try {
@@ -183,15 +169,12 @@
         }
 
         // Collapse "." and ".."
-        try {
-            path = GetFullPathName(path);
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(input);
-        }
-
-        // eliminate all symbolic links
-        if (resolveLinks) {
-            path = resolveAllLinks(WindowsPath.createFromNormalizedPath(fs, path));
+        if (path.indexOf('.') >= 0) {
+            try {
+                path = GetFullPathName(path);
+            } catch (WindowsException x) {
+                x.rethrowAsIOException(input);
+            }
         }
 
         // string builder to build up components of path
@@ -229,12 +212,15 @@
             throw new AssertionError("path type not recognized");
         }
 
-        // check root directory exists
-        try {
-            FirstFile fileData = FindFirstFile(sb.toString() + "*");
-            FindClose(fileData.handle());
-        } catch (WindowsException x) {
-            x.rethrowAsIOException(path);
+        // if the result is only a root component then we simply check it exists
+        if (start >= path.length()) {
+            String result = sb.toString();
+            try {
+                GetFileAttributes(result);
+            } catch (WindowsException x) {
+                x.rethrowAsIOException(path);
+            }
+            return result;
         }
 
         // iterate through each component to get its actual name in the
@@ -246,13 +232,28 @@
             String search = sb.toString() + path.substring(curr, end);
             try {
                 FirstFile fileData = FindFirstFile(addLongPathPrefixIfNeeded(search));
-                try {
-                    sb.append(fileData.name());
-                    if (next != -1) {
-                        sb.append('\\');
+                FindClose(fileData.handle());
+
+                // if a reparse point is encountered then we must return the
+                // final path.
+                if (resolveLinks &&
+                    WindowsFileAttributes.isReparsePoint(fileData.attributes()))
+                {
+                    String result = getFinalPath(input);
+                    if (result == null) {
+                        // Fallback to slow path, usually because there is a sym
+                        // link to a file system that doesn't support sym links.
+                        WindowsPath resolved = resolveAllLinks(
+                            WindowsPath.createFromNormalizedPath(fs, path));
+                        result = getRealPath(resolved, false);
                     }
-                } finally {
-                    FindClose(fileData.handle());
+                    return result;
+                }
+
+                // add the name to the result
+                sb.append(fileData.name());
+                if (next != -1) {
+                    sb.append('\\');
                 }
             } catch (WindowsException e) {
                 e.rethrowAsIOException(path);
@@ -342,7 +343,7 @@
     /**
      * Resolve all symbolic-links in a given absolute and normalized path
      */
-    private static String resolveAllLinks(WindowsPath path)
+    private static WindowsPath resolveAllLinks(WindowsPath path)
         throws IOException
     {
         assert path.isAbsolute();
@@ -401,7 +402,7 @@
             }
         }
 
-        return path.toString();
+        return path;
     }
 
     /**
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java	Thu Aug 20 08:39:18 2009 +0100
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java	Thu Aug 20 08:42:38 2009 +0100
@@ -180,10 +180,12 @@
     static class FirstFile {
         private long handle;
         private String name;
+        private int attributes;
 
         private FirstFile() { }
         public long handle()    { return handle; }
         public String name()    { return name; }
+        public int attributes() { return attributes; }
     }
     private static native void FindFirstFile0(long lpFileName, FirstFile obj)
         throws WindowsException;
--- a/jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c	Thu Aug 20 08:39:18 2009 +0100
+++ b/jdk/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c	Thu Aug 20 08:42:38 2009 +0100
@@ -48,6 +48,7 @@
  */
 static jfieldID findFirst_handle;
 static jfieldID findFirst_name;
+static jfieldID findFirst_attributes;
 
 static jfieldID findStream_handle;
 static jfieldID findStream_name;
@@ -134,6 +135,7 @@
     }
     findFirst_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
     findFirst_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
+    findFirst_attributes = (*env)->GetFieldID(env, clazz, "attributes", "I");
 
     clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstStream");
     if (clazz == NULL) {
@@ -371,6 +373,7 @@
             return;
         (*env)->SetLongField(env, obj, findFirst_handle, ptr_to_jlong(handle));
         (*env)->SetObjectField(env, obj, findFirst_name, name);
+        (*env)->SetIntField(env, obj, findFirst_attributes, data.dwFileAttributes);
     } else {
         throwWindowsException(env, GetLastError());
     }
@@ -387,7 +390,7 @@
     if (handle == INVALID_HANDLE_VALUE) {
         throwWindowsException(env, GetLastError());
     }
-        return ptr_to_jlong(handle);
+    return ptr_to_jlong(handle);
 }
 
 JNIEXPORT jstring JNICALL