diff --git a/bin/win/IdeaWin32.dll b/bin/win/IdeaWin32.dll index 9085f6512b2d..ae77ff9b2309 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 668ff24f5aca..62c674fab522 100644 Binary files a/bin/win/IdeaWin64.dll and b/bin/win/IdeaWin64.dll differ diff --git a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java index 6155b9fb01e5..f30863bc012e 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileSystemUtil.java @@ -15,6 +15,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -164,7 +165,7 @@ public class FileSystemUtil { else { realPath = ourMediator.resolveSymLink(path); } - if (realPath != null && new File(realPath).exists()) { + if (realPath != null && (SystemInfo.isWindows && realPath.startsWith("\\\\") || new File(realPath).exists())) { return realPath; } } @@ -364,18 +365,39 @@ public class FileSystemUtil { private final IdeaWin32 myInstance = IdeaWin32.getInstance(); @Override - protected FileAttributes getAttributes(@NotNull final String path) { - final FileInfo fileInfo = myInstance.getInfo(path); + protected FileAttributes getAttributes(@NotNull String path) { + FileInfo fileInfo = myInstance.getInfo(path); return fileInfo != null ? fileInfo.toFileAttributes() : null; } @Override - protected String resolveSymLink(@NotNull final String path) { - return myInstance.resolveSymLink(path); + protected String resolveSymLink(@NotNull String path) throws FileNotFoundException { + path = new File(path).getAbsolutePath(); + + char drive = Character.toUpperCase(path.charAt(0)); + if (!(path.length() > 3 && drive >= 'A' && drive <= 'Z' && path.charAt(1) == ':' && path.charAt(2) == '\\')) { + return path; // unknown format + } + + int remainder = 4; + while (remainder < path.length()) { + int next = path.indexOf('\\', remainder); + String subPath = next > 0 ? path.substring(0, next) : path; + FileAttributes attributes = getAttributes(subPath); + if (attributes == null) { + throw new FileNotFoundException(subPath); + } + if (attributes.isSymLink()) { + return myInstance.resolveSymLink(path); + } + + remainder = next > 0 ? next + 1 : path.length(); + } + + return path; } } - // thanks to SVNKit for the idea of platform-specific offsets private static class JnaUnixMediatorImpl extends Mediator { @SuppressWarnings({"OctalInteger", "SpellCheckingInspection"}) @@ -433,7 +455,7 @@ public class FileSystemUtil { private final boolean myCoarseTs = SystemProperties.getBooleanProperty(COARSE_TIMESTAMP_KEY, false); private JnaUnixMediatorImpl() { - if ("linux-x86".equals(Platform.RESOURCE_PREFIX)) myOffsets = LINUX_32; + if ("linux-x86".equals(Platform.RESOURCE_PREFIX)) myOffsets = LINUX_32; else if ("linux-x86-64".equals(Platform.RESOURCE_PREFIX)) myOffsets = LINUX_64; else if ("linux-arm".equals(Platform.RESOURCE_PREFIX)) myOffsets = LNX_ARM32; else if ("linux-ppc".equals(Platform.RESOURCE_PREFIX)) myOffsets = LNX_PPC32; 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 5e6a2849f725..10264227a0ec 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileAttributesReadingTest.java @@ -60,7 +60,9 @@ public abstract class FileAttributesReadingTest { @Override public void linkToDirectory() { } @Override public void missingLink() { } @Override public void selfLink() { } + @Override public void innerSymlinkResolve() { } @Override public void junction() { } + @Override public void innerJunctionResolve() { } @Override public void permissionsCloning() { } } @@ -292,13 +294,22 @@ public abstract class FileAttributesReadingTest { assertEquals(dir.getPath(), target); } + @Test + public void innerSymlinkResolve() throws IOException { + File file = tempDir.newFile("dir/file.txt"); + File link = new File(tempDir.getRoot(), "link"); + Files.createSymbolicLink(link.toPath(), file.getParentFile().toPath()); + + String target = FileSystemUtil.resolveSymLink(link.getPath() + '/' + file.getName()); + assertEquals(file.getPath(), target); + } + @Test public void junction() throws IOException { assumeTrue(SystemInfo.isWinVistaOrNewer); File target = tempDir.newFolder("dir"); - File path = new File(tempDir.getRoot(), "junction.dir"); - File junction = IoTestUtil.createJunction(target.getPath(), path.getAbsolutePath()); + File junction = IoTestUtil.createJunction(target.getPath(), tempDir.getRoot() + "/junction.dir"); try { FileAttributes attributes = getAttributes(junction); @@ -324,6 +335,18 @@ public abstract class FileAttributesReadingTest { } } + @Test + public void innerJunctionResolve() throws IOException { + assumeTrue(SystemInfo.isWinVistaOrNewer); + + File file = tempDir.newFile("dir/file.txt"); + File junction = new File(tempDir.getRoot(), "junction"); + IoTestUtil.createJunction(file.getParent(), junction.getPath()); + + String target = FileSystemUtil.resolveSymLink(junction.getPath() + '/' + file.getName()); + assertEquals(file.getPath(), target); + } + @Test public void hiddenDir() throws IOException { assumeTrue(SystemInfo.isWindows); @@ -535,7 +558,7 @@ public abstract class FileAttributesReadingTest { } @Test - public void testUnicodeName() throws IOException { + public void unicodeName() throws IOException { String name = IoTestUtil.getUnicodeName(); assumeTrue(name != null); File file = tempDir.newFile(name + ".txt"); @@ -560,9 +583,9 @@ public abstract class FileAttributesReadingTest { if (SystemInfo.isWindows && checkList) { String parent = file.getParent(); if (parent != null) { - FileInfo[] infos = IdeaWin32.getInstance().listChildren(parent); - assertNotNull(infos); - for (FileInfo info : infos) { + FileInfo[] children = IdeaWin32.getInstance().listChildren(parent); + assertNotNull(children); + for (FileInfo info : children) { if (file.getName().equals(info.getName())) { assertEquals(attributes, info.toFileAttributes()); return attributes;