diff --git a/bin/win/IdeaWin32.dll b/bin/win/IdeaWin32.dll index bb9c214e8025..16c7b25be82f 100644 Binary files a/bin/win/IdeaWin32.dll and b/bin/win/IdeaWin32.dll differ diff --git a/bin/win/IdeaWin64.dll b/bin/win/IdeaWin64.dll index 18b3e1493336..e5e8896b4550 100644 Binary files a/bin/win/IdeaWin64.dll and b/bin/win/IdeaWin64.dll differ diff --git a/native/IdeaWin32/IdeaWin32.cpp b/native/IdeaWin32/IdeaWin32.cpp index 838bc76460a0..276e50329d38 100644 --- a/native/IdeaWin32/IdeaWin32.cpp +++ b/native/IdeaWin32/IdeaWin32.cpp @@ -31,13 +31,6 @@ static jclass getFileInfoClass(JNIEnv *env) { return env->FindClass("com/intellij/openapi/util/io/win32/FileInfo"); } -static HANDLE FindFileInner(JNIEnv *env, jstring path, LPWIN32_FIND_DATA lpData) { - const jchar* str = env->GetStringChars(path, 0); - const HANDLE h = FindFirstFile((LPCWSTR)str, lpData); - env->ReleaseStringChars(path, str); - return h; -} - static bool CopyObjectArray(JNIEnv *env, jobjectArray dst, jobjectArray src, jint count) { for (int i = 0; i < count; i++) { jobject p = env->GetObjectArrayElement(src, i); @@ -142,11 +135,12 @@ JNIEXPORT void JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32_initIDs JNIEXPORT jobject JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32_getInfo0(JNIEnv *env, jobject method, jstring path) { + const jchar* pathStr = env->GetStringChars(path, 0); + WIN32_FILE_ATTRIBUTE_DATA attrData; - const jchar* str = env->GetStringChars(path, 0); - BOOL res = GetFileAttributesEx((LPCWSTR)str, GetFileExInfoStandard, &attrData); - env->ReleaseStringChars(path, str); + BOOL res = GetFileAttributesEx((LPCWSTR)pathStr, GetFileExInfoStandard, &attrData); if (!res) { + env->ReleaseStringChars(path, pathStr); return NULL; } @@ -159,7 +153,7 @@ JNIEXPORT jobject JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32_getI if (IS_SET(attrData.dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)) { WIN32_FIND_DATA findData; - HANDLE h = FindFileInner(env, path, &findData); + HANDLE h = FindFirstFile((LPCWSTR)pathStr, &findData); if (h != INVALID_HANDLE_VALUE) { FindClose(h); data.dwFileAttributes = findData.dwFileAttributes; @@ -170,6 +164,8 @@ JNIEXPORT jobject JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32_getI } } + env->ReleaseStringChars(path, pathStr); + jclass fileInfoClass = getFileInfoClass(env); if (fileInfoClass == NULL) { return NULL; @@ -184,58 +180,65 @@ JNIEXPORT jstring JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32_reso return NULL; // XP } - WIN32_FIND_DATA data; - HANDLE h = FindFileInner(env, path, &data); - if (h == INVALID_HANDLE_VALUE) { - return NULL; - } - FindClose(h); - - if (!IS_SET(data.dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) || !IS_SET(data.dwReserved0, IO_REPARSE_TAG_SYMLINK)) { - return NULL; - } - - const jchar* str = env->GetStringChars(path, 0); - HANDLE th = CreateFile((LPCWSTR)str, 0, FILE_SHARE_ATTRIBUTES, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - env->ReleaseStringChars(path, str); - if (th == INVALID_HANDLE_VALUE) { - return NULL; - } - + const jchar* pathStr = env->GetStringChars(path, 0); jstring result = NULL; - TCHAR name[MAX_PATH]; - DWORD len = __GetFinalPathNameByHandle(th, name, MAX_PATH, 0); - if (len > 0) { - if (len < MAX_PATH) { - result = env->NewString((jchar *)name, len); - } - else { - TCHAR *name = (TCHAR *)malloc(sizeof(TCHAR) * (len + 1)); - if (name != NULL) { - len = __GetFinalPathNameByHandle(th, name, len, 0); + WIN32_FIND_DATA data; + HANDLE h = FindFirstFile((LPCWSTR)pathStr, &data); + if (h != INVALID_HANDLE_VALUE) { + FindClose(h); + + if (IS_SET(data.dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) && IS_SET(data.dwReserved0, IO_REPARSE_TAG_SYMLINK)) { + HANDLE th = CreateFile((LPCWSTR)pathStr, 0, FILE_SHARE_ATTRIBUTES, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (th != INVALID_HANDLE_VALUE) { + TCHAR name[MAX_PATH]; + DWORD len = __GetFinalPathNameByHandle(th, name, MAX_PATH, 0); if (len > 0) { - result = env->NewString((jchar *)name, len); + if (len < MAX_PATH) { + result = env->NewString((jchar *)name, len); + } + else { + TCHAR *name = (TCHAR *)malloc(sizeof(TCHAR) * (len + 1)); + if (name != NULL) { + len = __GetFinalPathNameByHandle(th, name, len, 0); + if (len > 0) { + result = env->NewString((jchar *)name, len); + } + free(name); + } + } } - free(name); + CloseHandle(th); + } } } - CloseHandle(th); + env->ReleaseStringChars(path, pathStr); return result; } JNIEXPORT jobjectArray JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32_listChildren0(JNIEnv *env, jobject method, jstring path) { - WIN32_FIND_DATA data; - HANDLE h = FindFileInner(env, path, &data); - if (h == INVALID_HANDLE_VALUE) { + jclass fileInfoClass = getFileInfoClass(env); + if (fileInfoClass == NULL) { return NULL; } - jclass fileInfoClass = getFileInfoClass(env); - if (fileInfoClass == NULL) { + size_t pathLen = env->GetStringLength(path) + 3; + wchar_t* pathStr = (wchar_t *)malloc(pathLen * sizeof(wchar_t)); + if (pathStr == NULL) { + return NULL; + } + const jchar* str = env->GetStringChars(path, 0); + wcscpy_s(pathStr, pathLen, (LPCWSTR)str); + wcscat_s(pathStr, pathLen, L"\\*"); + env->ReleaseStringChars(path, str); + + WIN32_FIND_DATA data; + HANDLE h = FindFirstFile((LPCWSTR)pathStr, &data); + if (h == INVALID_HANDLE_VALUE) { + free(pathStr); return NULL; } @@ -261,17 +264,20 @@ JNIEXPORT jobjectArray JNICALL Java_com_intellij_openapi_util_io_win32_IdeaWin32 } while (FindNextFile(h, &data)); - FindClose(h); + free(pathStr); + FindClose(h); old = rv; rv = env->NewObjectArray(len, fileInfoClass, NULL); if (rv == NULL || !CopyObjectArray(env, rv, old, len)) { goto error; } + return rv; error: - FindClose(h); + free(pathStr); + FindClose(h); return NULL; } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java index eae60f9a094c..b52cdd0cfd94 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/vfs/local/SymlinkHandlingTest.java @@ -109,11 +109,14 @@ public class SymlinkHandlingTest extends LightPlatformLangTestCase { assertEquals(upDir.getPath(), upLinkVFile.getCanonicalPath()); assertVisitedPaths(upDir.getPath(), upLinkVFile.getPath()); - final File nestedLinksFile = new File(upDir.getPath() + StringUtil.repeat(File.separator + upLinkFile.getName(), 4)); - assertTrue(nestedLinksFile.getPath(), nestedLinksFile.isDirectory()); - final VirtualFile nestedLinksVFile = refreshAndFind(nestedLinksFile); - assertNotNull(nestedLinksFile.getPath(), nestedLinksVFile); - assertEquals(upLinkVFile.getCanonicalFile(), nestedLinksVFile.getCanonicalFile()); + final File repeatedLinksFile = new File(upDir.getPath() + StringUtil.repeat(File.separator + upLinkFile.getName(), 4)); + assertTrue(repeatedLinksFile.getPath(), repeatedLinksFile.isDirectory()); + final VirtualFile repeatedLinksVFile = refreshAndFind(repeatedLinksFile); + assertNotNull(repeatedLinksFile.getPath(), repeatedLinksVFile); + assertTrue(repeatedLinksVFile.isSymLink()); + assertTrue(repeatedLinksVFile.isDirectory()); + assertEquals(upDir.getPath(), repeatedLinksVFile.getCanonicalPath()); + assertEquals(upLinkVFile.getCanonicalFile(), repeatedLinksVFile.getCanonicalFile()); } public void testMutualRecursiveLinks() throws Exception { diff --git a/platform/util/src/com/intellij/openapi/util/io/win32/IdeaWin32.java b/platform/util/src/com/intellij/openapi/util/io/win32/IdeaWin32.java index 6ac6deaf17c6..9aa1cc66fa0e 100644 --- a/platform/util/src/com/intellij/openapi/util/io/win32/IdeaWin32.java +++ b/platform/util/src/com/intellij/openapi/util/io/win32/IdeaWin32.java @@ -36,7 +36,6 @@ public class IdeaWin32 { private static final String PATH_PREFIX = "\\\\?\\"; private static final int PREFIX_SIZE = PATH_PREFIX.length(); - private static final String PATH_SUFFIX = "\\*.*"; private static final int MAX_PATH = 260; private static final IdeaWin32 ourInstance; @@ -111,7 +110,7 @@ public class IdeaWin32 { @Nullable public FileInfo[] listChildren(@NotNull final String path) { LogUtil.debug(LOG, "list(%s)", path); - return listChildren0(path(path) + PATH_SUFFIX); + return listChildren0(path(path)); } private static String path(final String path) { diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java index e4f70421b128..da4a1973cc69 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java @@ -208,6 +208,22 @@ public class FileAttributesReadingTest { assertNull(target); } + @Test + public void selfLink() throws Exception { + assumeTrue(SystemInfo.areSymLinksSupported); + + final File dir = FileUtil.createTempDirectory(myTempDirectory, "test.", ".dir"); + final File link = IoTestUtil.createTempLink(dir.getPath(), new File(dir, "link").getPath()); + + final FileAttributes attributes = getAttributes(link); + assertEquals(FileAttributes.Type.DIRECTORY, attributes.type); + assertEquals(FileAttributes.SYM_LINK, attributes.flags); + assertTimestampsEqual(dir.lastModified(), attributes.lastModified); + + final String target = FileSystemUtil.resolveSymLink(link); + assertEquals(dir.getPath(), target); + } + @Test public void junction() throws Exception { assumeTrue(SystemInfo.isWindows); @@ -235,7 +251,7 @@ public class FileAttributesReadingTest { final File file = new File("C:\\Documents and Settings\\desktop.ini"); assumeTrue(file.exists()); - final FileAttributes attributes = getAttributes(file); + final FileAttributes attributes = getAttributes(file, false); assertEquals(FileAttributes.Type.FILE, attributes.type); assertEquals(FileAttributes.HIDDEN, attributes.flags); assertEquals(file.length(), attributes.length); @@ -302,9 +318,30 @@ public class FileAttributesReadingTest { @NotNull private static FileAttributes getAttributes(@NotNull final File file) { + return getAttributes(file, true); + } + + @NotNull + private static FileAttributes getAttributes(@NotNull final File file, final boolean checkList) { final FileAttributes attributes = FileSystemUtil.getAttributes(file); assertNotNull(attributes); System.out.println(attributes); + + if (SystemInfo.isWindows && checkList) { + final String parent = file.getParent(); + if (parent != null) { + final FileInfo[] infos = IdeaWin32.getInstance().listChildren(parent); + assertNotNull(infos); + for (FileInfo info : infos) { + if (file.getName().equals(info.getName())) { + assertEquals(attributes, info.toFileAttributes()); + return attributes; + } + } + fail(file + " not listed"); + } + } + return attributes; }