From bc261675cab7587ae512385f4d3b001620b0f795 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Tue, 30 Jun 2026 14:10:47 +0200 Subject: [PATCH] Preparation for the faster (bulk) LocalFile.childInfos() implementation Extracted non-native refactorings from https://github.com/eclipse-platform/eclipse.platform/pull/2779 to a dedicated commit. Functionality shouldn't be changed. --- .../internal/filesystem/local/LocalFile.java | 8 +++++-- .../local/LocalFileNativesManager.java | 8 +++++++ .../filesystem/local/NativeHandler.java | 18 ++++++++++++++ .../filesystem/local/unix/StructStat.java | 21 +++++++++++++++- .../local/unix/UnixFileNatives.java | 24 ++++++++++--------- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFile.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFile.java index f2bda5e0584..3fc03131d99 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFile.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFile.java @@ -135,10 +135,14 @@ private void checkTargetIsNotWritable(File target, Throwable exception) throws C } } + @Override + public IFileInfo[] childInfos(int options, IProgressMonitor monitor) { + return LocalFileNativesManager.listDirectoryAndGetFileInfos(filePath); + } + @Override public String[] childNames(int options, IProgressMonitor monitor) { - String[] names = file.list(); - return (names == null ? EMPTY_STRING_ARRAY : names); + return LocalFileNativesManager.listDirectoryNames(filePath); } @Override diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFileNativesManager.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFileNativesManager.java index 9900dfe253f..89ab0df082e 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFileNativesManager.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFileNativesManager.java @@ -87,4 +87,12 @@ public static boolean putFileInfo(String fileName, IFileInfo info, int options) return HANDLER.putFileInfo(fileName, info, options); } + public static IFileInfo[] listDirectoryAndGetFileInfos(String fileName) { + return HANDLER.listDirectoryAndGetFileInfos(fileName); + } + + public static String[] listDirectoryNames(String fileName) { + return HANDLER.listDirectoryNames(fileName); + } + } diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/NativeHandler.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/NativeHandler.java index 3e144fe6b79..6ea955cd605 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/NativeHandler.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/NativeHandler.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.eclipse.core.internal.filesystem.local; +import java.io.File; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.provider.FileInfo; @@ -25,4 +26,21 @@ public abstract class NativeHandler { public abstract FileInfo fetchFileInfo(String fileName); public abstract boolean putFileInfo(String fileName, IFileInfo info, int options); + + protected static final String[] EMPTY_STRING_ARRAY = {}; + + public String[] listDirectoryNames(String fileName) { + String[] names = new File(fileName).list(); + return names == null ? EMPTY_STRING_ARRAY : names; + } + + public IFileInfo[] listDirectoryAndGetFileInfos(String fileName) { + var directoryContents = listDirectoryNames(fileName); + var result = new IFileInfo[directoryContents.length]; + for (int i = 0; i < directoryContents.length; i++) { + result[i] = fetchFileInfo(fileName + File.separator + directoryContents[i]); + } + return result; + } + } diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java index e53ba661727..598575ab16f 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/StructStat.java @@ -14,6 +14,7 @@ package org.eclipse.core.internal.filesystem.local.unix; import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.provider.FileInfo; /** @@ -21,6 +22,8 @@ * and is used by JNI calls wrapping OS file related functions. */ public class StructStat { + /** errno value for "No such file or directory" */ + public static final int ENOENT = 2; private static final boolean USE_MILLISECOND_RESOLUTION = Boolean.parseBoolean(System.getProperty("eclipse.filesystem.useNatives.modificationTimestampMillisecondsResolution", "true")); //$NON-NLS-1$ //$NON-NLS-2$ @@ -29,10 +32,20 @@ public class StructStat { public long st_mtime; public long st_mtime_msec; // millisecond component of the file timestamp, filled on Linux systems public long st_flags; // Filled only on Mac OS X + public int errno; + public String name; + public String linkFile; // Filled target for symbolic links public FileInfo toFileInfo() { FileInfo info = new FileInfo(); - info.setExists(true); + if (errno != 0 && errno != ENOENT) { + info.setError(IFileInfo.IO_ERROR); + return info; + } + info.setExists(errno != ENOENT); + if (name != null) { + info.setName(name); + } info.setLength(st_size); long lastModified = (st_mtime * 1_000); if (USE_MILLISECOND_RESOLUTION) { @@ -42,6 +55,12 @@ public FileInfo toFileInfo() { if ((st_mode & UnixFileFlags.S_IFMT) == UnixFileFlags.S_IFDIR) { info.setDirectory(true); } + if (linkFile != null) { + info.setAttribute(EFS.ATTRIBUTE_SYMLINK, true); + if (!linkFile.isEmpty()) { + info.setStringAttribute(EFS.ATTRIBUTE_LINK_TARGET, linkFile); + } + } if ((st_flags & (UnixFileFlags.UF_IMMUTABLE | UnixFileFlags.SF_IMMUTABLE)) != 0) { info.setAttribute(EFS.ATTRIBUTE_IMMUTABLE, true); } diff --git a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java index 5b030b14543..ddb3cc9a947 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileNatives.java @@ -21,7 +21,9 @@ import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.provider.FileInfo; -import org.eclipse.core.internal.filesystem.*; +import org.eclipse.core.internal.filesystem.FileSystemAccess; +import org.eclipse.core.internal.filesystem.Messages; +import org.eclipse.core.internal.filesystem.Policy; import org.eclipse.core.internal.filesystem.local.Convert; import org.eclipse.core.runtime.IStatus; import org.eclipse.osgi.util.NLS; @@ -30,7 +32,7 @@ public abstract class UnixFileNatives { private static final String LIBRARY_NAME = "unixfile_1_0_0"; //$NON-NLS-1$ private static final int UNICODE_SUPPORTED = 1 << 0; private static final int CHFLAGS_SUPPORTED = 1 << 1; - private static final int ENOENT = 2; // errno value for "No such file or directory" + private static final int ENOENT = StructStat.ENOENT; // errno value for "No such file or directory" private static final boolean usingNatives; private static final int libattr; @@ -80,23 +82,23 @@ public static FileInfo fetchFileInfo(String fileName) { FileInfo info = null; byte[] name = fileNameToBytes(fileName); StructStat stat = new StructStat(); - if (lstat(name, stat) == 0) { - if ((stat.st_mode & UnixFileFlags.S_IFMT) == UnixFileFlags.S_IFLNK) { - if (stat(name, stat) == 0) { - info = stat.toFileInfo(); - } else { + if (lstat(name, stat) == 0) { // return information about the link itself if the file is a symbolic link + if ((stat.st_mode & UnixFileFlags.S_IFMT) == UnixFileFlags.S_IFLNK) { // it a link! + if (stat(name, stat) == 0) { // get the information about the file the link points to + info = stat.toFileInfo(); // store the target file stats in info + } else { // invalid link target! info = new FileInfo(); if (getErrno() != ENOENT) { info.setError(IFileInfo.IO_ERROR); } } - info.setAttribute(EFS.ATTRIBUTE_SYMLINK, true); + info.setAttribute(EFS.ATTRIBUTE_SYMLINK, true); // set symlink attribute byte target[] = new byte[UnixFileFlags.PATH_MAX]; int length = readlink(name, target, target.length); - if (length > 0) { + if (length > 0) { // set target of the link info.setStringAttribute(EFS.ATTRIBUTE_LINK_TARGET, bytesToFileName(target, length)); } - } else { + } else { // regular file or directory info = stat.toFileInfo(); } } else { @@ -106,7 +108,7 @@ public static FileInfo fetchFileInfo(String fileName) { } } - if (info.getName() == null) { + if (info.getName().isEmpty()) { // If the file system is case insensitive, we don't know the real name of the file. // Since obtaining the real name in such situation is pretty expensive, we use the name // passed as a parameter, which may differ by case from the real name of the file