From 9ba8b8323f7f36ccdaff1631f8bedacbb050336d Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Sun, 20 Sep 2026 16:57:20 +0200 Subject: [PATCH] Fix handling of DOS device paths in Win32Handler and unify Path handling The method Win32Handler.toLongWindowsPath() handled file paths incorrectly if they are already DOS device paths to an ordinary file, i.e. started with \\?\, e.g. a path like \\?\C:\User\Foo\bar.txt. In real world scenarios this was probably of little relevance since the Eclipse platform usually does not pass such DOS device paths. --- .../internal/filesystem/local/Convert.java | 3 -- .../filesystem/local/Win32Handler.java | 28 +++++++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Convert.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Convert.java index ebe2c63b9eb..212c46f0914 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Convert.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Convert.java @@ -22,9 +22,6 @@ public class Convert { /** Indicates the default native encoding on this platform */ private static String defaultEncoding = Platform.getSystemCharset().name(); - public static final String WIN32_RAW_PATH_PREFIX = "\\\\?\\"; //$NON-NLS-1$ - public static final String WIN32_UNC_RAW_PATH_PREFIX = "\\\\?\\UNC"; //$NON-NLS-1$ - /** * Calling new String(byte[] s) creates a new encoding object and other garbage. * This can be avoided by calling new String(byte[] s, String encoding) instead. diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Win32Handler.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Win32Handler.java index ff02136b0b1..bfc16b5bdb8 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Win32Handler.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/Win32Handler.java @@ -14,9 +14,6 @@ *******************************************************************************/ package org.eclipse.core.internal.filesystem.local; -import static org.eclipse.core.internal.filesystem.local.Convert.WIN32_RAW_PATH_PREFIX; -import static org.eclipse.core.internal.filesystem.local.Convert.WIN32_UNC_RAW_PATH_PREFIX; - import com.microsoft.windows.FILETIME; import com.microsoft.windows.FileAPI; import com.microsoft.windows.WIN32_FIND_DATAW; @@ -48,6 +45,10 @@ * See the implementation notes for the rational for direct native method invocations. */ public class Win32Handler extends NativeHandler { + + private static final String WIN32_RAW_PATH_PREFIX = "\\\\?\\"; //$NON-NLS-1$ + private static final String WIN32_UNC_RAW_PATH_PREFIX = "\\\\?\\UNC"; //$NON-NLS-1$ + private static final int ATTRIBUTES = EFS.ATTRIBUTE_SYMLINK | EFS.ATTRIBUTE_LINK_TARGET // symbolic link support | EFS.ATTRIBUTE_ARCHIVE | EFS.ATTRIBUTE_READ_ONLY | EFS.ATTRIBUTE_HIDDEN; // standard DOS attributes @@ -73,12 +74,13 @@ public FileInfo fetchFileInfo(String fileName) { FileInfo fileInfo = new FileInfo(); String target = toLongWindowsPath(fileName); + Path file = Path.of(target); if (target.length() == 7 && target.startsWith(WIN32_RAW_PATH_PREFIX) && target.endsWith(":\\")) { //$NON-NLS-1$ // FindFirstFile does not work at the root level. However, we don't need it because the root will never change time-stamp. // A root path is for example: \\?\c:\ fileInfo.setDirectory(true); - fileInfo.setExists(Files.exists(Path.of(target.substring(WIN32_RAW_PATH_PREFIX.length())))); + fileInfo.setExists(Files.exists(file)); return fileInfo; } try (Arena arena = Arena.ofConfined()) { @@ -100,7 +102,7 @@ public FileInfo fetchFileInfo(String fileName) { } FileAPI.FindClose(handle); - convertFindDataWToFileInfo(lpFindFileData, fileInfo, fileName); + convertFindDataWToFileInfo(lpFindFileData, fileInfo, file); } catch (IOException e) { // Leave alone and continue. The name is set before an IOException can be thrown fileInfo.setError(IFileInfo.IO_ERROR); @@ -111,7 +113,7 @@ public FileInfo fetchFileInfo(String fileName) { private static final StructLayout LAST_ERROR_CAPTURE_LAYOUT = Linker.Option.captureStateLayout(); private static final VarHandle GET_LAST_ERROR_HANDLE = LAST_ERROR_CAPTURE_LAYOUT.varHandle(// MemoryLayout.PathElement.groupElement("GetLastError")); //$NON-NLS-1$ - private static final MethodHandle FIND_FIRST_FILE__W_HANDLE = Linker.nativeLinker().downcallHandle( // + private static final MethodHandle FIND_FIRST_FILE_W_HANDLE = Linker.nativeLinker().downcallHandle( // FileAPI.FindFirstFileW$address(), FileAPI.FindFirstFileW$descriptor(), // Linker.Option.captureCallState("GetLastError")); //$NON-NLS-1$ @@ -126,7 +128,7 @@ public FileInfo fetchFileInfo(String fileName) { */ private static MemorySegment FindFirstFileW(MemorySegment lpFileName, MemorySegment lpFindFileData, MemorySegment capturedError) { try { - return (MemorySegment) FIND_FIRST_FILE__W_HANDLE.invokeExact(capturedError, lpFileName, lpFindFileData); + return (MemorySegment) FIND_FIRST_FILE_W_HANDLE.invokeExact(capturedError, lpFileName, lpFindFileData); } catch (Error | RuntimeException e) { throw e; } catch (Throwable e) { @@ -199,18 +201,20 @@ public static String getShortPathName(String longPath) { private static String toLongWindowsPath(String fileName) { // See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file - if (fileName.startsWith("\\\\") && !fileName.startsWith(WIN32_UNC_RAW_PATH_PREFIX)) { //$NON-NLS-1$ + // https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#dos-device-paths + if (fileName.startsWith(WIN32_RAW_PATH_PREFIX)) { + return fileName; + } else if (fileName.startsWith("\\\\")) { //$NON-NLS-1$ //convert UNC path of form \\server\path to long/unicode form \\?\UNC\server\path return WIN32_UNC_RAW_PATH_PREFIX + fileName.substring(1); - } else if (!fileName.startsWith(WIN32_RAW_PATH_PREFIX)) { + } else { //convert simple path of form C:\path to long/unicode form \\?\C:\path return WIN32_RAW_PATH_PREFIX + fileName; } - return fileName; } @SuppressWarnings("static-access") - private static void convertFindDataWToFileInfo(MemorySegment mem, FileInfo info, String fileName) throws IOException { + private static void convertFindDataWToFileInfo(MemorySegment mem, FileInfo info, Path file) throws IOException { /** * For possible values of dwFileAttributes and their descriptions, * see File Attribute Constants. @@ -236,7 +240,7 @@ private static void convertFindDataWToFileInfo(MemorySegment mem, FileInfo info, boolean isReparsePoint = isSet(dwFileAttributes, FileAPI.FILE_ATTRIBUTE_REPARSE_POINT()); if (isReparsePoint && dwReserved0 == FileAPI.IO_REPARSE_TAG_SYMLINK()) { - Path linkTarget = Files.readSymbolicLink(Path.of(fileName)); + Path linkTarget = Files.readSymbolicLink(file); info.setAttribute(EFS.ATTRIBUTE_SYMLINK, true); info.setStringAttribute(EFS.ATTRIBUTE_LINK_TARGET, linkTarget.toString()); }