From d5fc213672a67e339a4cc89809f0cece7d4911e7 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Fri, 19 Jun 2026 17:26:11 +0200 Subject: [PATCH 1/2] [DRAFT] Introduce faster (batched) LocalFile.childInfos() version The code is supposed to speed up NFS file access on Linux by an order of magnitude by fetching directory children data (IFileInfo) in one shot from JNI (via array of StructStat). LocalFile.childInfos(int, IProgressMonitor) overrides now the FileStore.childInfos(int, IProgressMonitor) on Linux with an optimized version. No idea how to build binary for Mac without Mac. --- .../os/linux/x86_64/libunixfile_1_0_0.so | Bin 10320 -> 18800 bytes .../natives/unix/unixfile.c | 355 +++++++++++++++++- .../natives/unix/unixfile.h | 18 +- .../internal/filesystem/local/LocalFile.java | 8 +- .../local/LocalFileNativesManager.java | 8 + .../filesystem/local/NativeHandler.java | 18 + .../filesystem/local/unix/StructStat.java | 20 +- .../local/unix/UnixFileHandler.java | 11 + .../local/unix/UnixFileNatives.java | 50 ++- 9 files changed, 464 insertions(+), 24 deletions(-) diff --git a/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.so b/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/os/linux/x86_64/libunixfile_1_0_0.so index 9067085dfacebbed918a3fa3fc3a15de2cff2e08..cdf1e34361880edb09dff7df3b3aec34093ab13a 100755 GIT binary patch literal 18800 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 diff --git a/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c b/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c index a23a89aa67a..395a495ecee 100644 --- a/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c +++ b/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #if defined MACOSX #include @@ -38,6 +40,14 @@ static jfieldID attrs_st_mtime_msec; /* Only filled on MACOSX. */ static jfieldID attrs_st_flags; +static jfieldID errno_fieldID; + +static jfieldID nameFieldId; + +static jfieldID linkFieldId; + +static const jsize INITIAL_LIST_ARRAY_SIZE = 100; + /* * Class: Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_initializeStructStatFieldIDs * Method: initializeStructStatFieldIDs @@ -51,6 +61,9 @@ JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_Unix attrs_st_mode = (*env)->GetFieldID(env, structStatClass, "st_mode", "I"); attrs_st_size = (*env)->GetFieldID(env, structStatClass, "st_size", "J"); attrs_st_mtime = (*env)->GetFieldID(env, structStatClass, "st_mtime", "J"); + errno_fieldID = (*env)->GetFieldID(env, structStatClass, "errno", "I"); + nameFieldId = (*env)->GetFieldID(env, structStatClass, "name", "Ljava/lang/String;"); + linkFieldId = (*env)->GetFieldID(env, structStatClass, "linkFile", "Ljava/lang/String;"); #ifdef MACOSX attrs_st_flags = (*env)->GetFieldID(env, structStatClass, "st_flags", "J"); @@ -80,17 +93,37 @@ jbyte* getByteArray(JNIEnv *env, jbyteArray target) } /* - * Fills StructStat object with data from struct stat. + * Copy object array contents using java.lang.System.arraycopy. */ -jint convertStatToObject(JNIEnv *env, struct stat info, jobject stat_object) +jint copyObjectArray(JNIEnv *env, jobject source, jobject target, jsize length) { - jclass cls; - jfieldID fid; - jboolean readOnly; + jclass systemClass; + jmethodID arraycopyMethod; + + systemClass = (*env)->FindClass(env, "java/lang/System"); + if (systemClass == NULL) { + return -1; + } + arraycopyMethod = (*env)->GetStaticMethodID(env, systemClass, "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V"); + if (arraycopyMethod == NULL) { + (*env)->DeleteLocalRef(env, systemClass); + return -1; + } - cls = (*env)->GetObjectClass(env, stat_object); - if (cls == 0) return -1; + (*env)->CallStaticVoidMethod(env, systemClass, arraycopyMethod, source, 0, target, 0, length); + (*env)->DeleteLocalRef(env, systemClass); + if ((*env)->ExceptionCheck(env)){ + return -1; + } + + return 0; +} +/* + * Fills StructStat object with data from struct stat. + */ +jint convertStatToObject(JNIEnv *env, struct stat info, int errnoValue, jobject stat_object) +{ if (attrs_st_mode == 0) return -1; (*env)->SetIntField(env, stat_object, attrs_st_mode, info.st_mode); @@ -100,6 +133,9 @@ jint convertStatToObject(JNIEnv *env, struct stat info, jobject stat_object) if (attrs_st_mtime == 0) return -1; (*env)->SetLongField(env, stat_object, attrs_st_mtime, info.st_mtime); + if (errno_fieldID == 0) return -1; + (*env)->SetIntField(env, stat_object, errno_fieldID, errnoValue); + #ifndef MACOSX if (attrs_st_mtime_msec == 0) return -1; (*env)->SetLongField(env, stat_object, attrs_st_mtime_msec, (info.st_mtim.tv_nsec / (1000 * 1000))); @@ -165,10 +201,10 @@ JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_Unix struct stat info; name = (char*) getByteArray(env, path); - code = stat(name, &info); + code = fstatat(AT_FDCWD, name, &info, 0); free(name); if (code != -1) - return convertStatToObject(env, info, buf); + return convertStatToObject(env, info, 0, buf); else return code; } @@ -186,10 +222,10 @@ JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_Unix struct stat info; name = (char*) getByteArray(env, path); - code = lstat(name, &info); + code = fstatat(AT_FDCWD, name, &info, AT_SYMLINK_NOFOLLOW); free(name); if (code != -1) - return convertStatToObject(env, info, buf); + return convertStatToObject(env, info, 0, buf); else return code; } @@ -329,3 +365,300 @@ JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_Unix free(flag); return ret; } + +JNIEXPORT jobjectArray JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_listDir + (JNIEnv *env, jclass, jbyteArray path) +{ + char *name; + DIR *dir = NULL; + int directoryFd; + jsize arrayLength; + int count = 0; + int i; + struct dirent *entry; + jobjectArray namesArray; + jobjectArray result = NULL; + jclass stringClass; + + stringClass = (*env)->FindClass(env, "java/lang/String"); + if (stringClass == NULL) { + return NULL; + } + namesArray = (*env)->NewObjectArray(env, INITIAL_LIST_ARRAY_SIZE, stringClass, NULL); + if (namesArray == NULL) { + return NULL; + } + arrayLength = INITIAL_LIST_ARRAY_SIZE; + + name = (char*) getByteArray(env, path); + dir = opendir(name); + free(name); + if (dir == NULL) { + goto cleanup; + } + directoryFd = dirfd(dir); + if (directoryFd == -1) { + goto cleanup; + } + while ((entry = readdir(dir)) != NULL) { + jstring nameString = NULL; + jobjectArray grownArray; + + /* Skip . and .. */ + if (entry->d_name[0] == '.' && + (entry->d_name[1] == '\0' || + (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) { + continue; + } + + if (count >= arrayLength) { + jsize newLength = arrayLength > 0 ? arrayLength * 2 : 1; + + if (newLength < arrayLength) { + goto cleanup; + } + + grownArray = (*env)->NewObjectArray(env, newLength, stringClass, NULL); + if (grownArray == NULL) { + goto cleanup; + } + + if (copyObjectArray(env, namesArray, grownArray, arrayLength) != 0) { + (*env)->DeleteLocalRef(env, grownArray); + goto cleanup; + } + + (*env)->DeleteLocalRef(env, namesArray); + namesArray = grownArray; + arrayLength = newLength; + } + + /* Add the directory entry name to the names array */ + nameString = (*env)->NewStringUTF(env, entry->d_name); + if (nameString == NULL) { + goto cleanup; + } + + (*env)->SetObjectArrayElement(env, namesArray, count, nameString); + if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, nameString); + goto cleanup; + } + (*env)->DeleteLocalRef(env, nameString); + + count++; + } + + result = (*env)->NewObjectArray(env, count, stringClass, NULL); + if (result == NULL) { + goto cleanup; + } + + for (i = 0; i < count; i++) { + jobject existingName = (*env)->GetObjectArrayElement(env, namesArray, i); + if ((*env)->ExceptionCheck(env)) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, result, i, existingName); + (*env)->DeleteLocalRef(env, existingName); + if ((*env)->ExceptionCheck(env)) { + goto cleanup; + } + } + +cleanup: + if (dir != NULL) { + closedir(dir); + } + return result; +} + +JNIEXPORT jobjectArray JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_listDirAndGetFileInfos + (JNIEnv *env, jclass, jbyteArray path) +{ + char *name; + DIR *dir = NULL; + int directoryFd; + jsize arrayLength; + int count = 0; + int i; + struct dirent *entry; + jclass structStatClass; + jmethodID structStatCtor; + jobjectArray statArray = NULL; + jobjectArray resultStatsArray = NULL; + jobjectArray result = NULL; + + structStatClass = (*env)->FindClass(env, "org/eclipse/core/internal/filesystem/local/unix/StructStat"); + if (structStatClass == NULL) { + return NULL; + } + + statArray = (*env)->NewObjectArray(env, INITIAL_LIST_ARRAY_SIZE, structStatClass, NULL); + if (statArray == NULL) { + return NULL; + } + arrayLength = INITIAL_LIST_ARRAY_SIZE; + + structStatCtor = (*env)->GetMethodID(env, structStatClass, "", "()V"); + if (structStatCtor == NULL) { + return NULL; + } + name = (char*) getByteArray(env, path); + dir = opendir(name); + free(name); + if (dir == NULL) { + goto cleanup; + } + directoryFd = dirfd(dir); + if (directoryFd == -1) { + goto cleanup; + } + while ((entry = readdir(dir)) != NULL) { + struct stat st; + jobject statObject = NULL; + jstring nameString = NULL; + int statErrno = 0; + + /* Skip . and .. */ + if (entry->d_name[0] == '.' && + (entry->d_name[1] == '\0' || + (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) { + continue; + } + + if (count >= arrayLength) { + jsize newLength = arrayLength > 0 ? arrayLength * 2 : 1; + jobjectArray grownStatArray; + + if (newLength < arrayLength) { + goto cleanup; + } + + grownStatArray = (*env)->NewObjectArray(env, newLength, structStatClass, NULL); + if (grownStatArray == NULL) { + goto cleanup; + } + + if (copyObjectArray(env, statArray, grownStatArray, arrayLength) != 0) { + (*env)->DeleteLocalRef(env, grownStatArray); + goto cleanup; + } + + (*env)->DeleteLocalRef(env, statArray); + + statArray = grownStatArray; + arrayLength = newLength; + } + + statObject = (*env)->NewObject(env, structStatClass, structStatCtor); + if (statObject == NULL) { + goto cleanup; + } + + /* Collect file stats. Keep processing even if stat/readlink fails. */ + if (fstatat(directoryFd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { + memset(&st, 0, sizeof(st)); + statErrno = errno; + } else if (S_ISLNK(st.st_mode)) { + char linkPath[PATH_MAX + 1]; + jstring linkString = NULL; + ssize_t linkPathLen; + + /* Follow symlink and update stat for further processing. */ + if (fstatat(directoryFd, entry->d_name, &st, 0) != 0) { + memset(&st, 0, sizeof(st)); + statErrno = errno; + } + + /* Read link target if possible. */ + linkPathLen = readlinkat(directoryFd, entry->d_name, linkPath, PATH_MAX); + if (linkPathLen >= 0) { + linkPath[linkPathLen] = '\0'; + } else if (statErrno == 0) { + statErrno = errno; + } + + if (linkPathLen >= 0) { + linkString = (*env)->NewStringUTF(env, linkPath); + } else { + linkString = (*env)->NewStringUTF(env, ""); + } + + if (linkString == NULL) { + (*env)->DeleteLocalRef(env, statObject); + goto cleanup; + } + (*env)->SetObjectField(env, statObject, linkFieldId, linkString); + (*env)->DeleteLocalRef(env, linkString); + if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, statObject); + goto cleanup; + } + } + + if (convertStatToObject(env, st, statErrno, statObject) != 0) { + (*env)->DeleteLocalRef(env, statObject); + goto cleanup; + } + + (*env)->SetObjectArrayElement(env, statArray, count, statObject); + if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, statObject); + goto cleanup; + } + + nameString = (*env)->NewStringUTF(env, entry->d_name); + if (nameString == NULL) { + (*env)->DeleteLocalRef(env, statObject); + goto cleanup; + } + + (*env)->SetObjectField(env, statObject, nameFieldId, nameString); + if ((*env)->ExceptionCheck(env)) { + (*env)->DeleteLocalRef(env, nameString); + (*env)->DeleteLocalRef(env, statObject); + goto cleanup; + } + (*env)->DeleteLocalRef(env, nameString); + (*env)->DeleteLocalRef(env, statObject); + + count++; + } + + resultStatsArray = (*env)->NewObjectArray(env, count, structStatClass, NULL); + if (resultStatsArray == NULL) { + goto cleanup; + } + + for (i = 0; i < count; i++) { + jobject existingStat = (*env)->GetObjectArrayElement(env, statArray, i); + if ((*env)->ExceptionCheck(env)) { + if (existingStat != NULL) { + (*env)->DeleteLocalRef(env, existingStat); + } + goto cleanup; + } + + (*env)->SetObjectArrayElement(env, resultStatsArray, i, existingStat); + (*env)->DeleteLocalRef(env, existingStat); + if ((*env)->ExceptionCheck(env)) { + goto cleanup; + } + } + + result = resultStatsArray; + resultStatsArray = NULL; + +cleanup: + if (dir != NULL) { + closedir(dir); + } + if (statArray != NULL) { + (*env)->DeleteLocalRef(env, statArray); + } + if (resultStatsArray != NULL) { + (*env)->DeleteLocalRef(env, resultStatsArray); + } + return result; +} \ No newline at end of file diff --git a/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h b/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h index 8fc4a836457..d6f427faed8 100644 --- a/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h +++ b/resources/bundles/org.eclipse.core.filesystem/natives/unix/unixfile.h @@ -27,7 +27,7 @@ jbyte* getByteArray(JNIEnv *, jbyteArray); /* * Fills StructStat object with data from struct stat. */ -jint convertStatToObject(JNIEnv *, struct stat, jobject); +jint convertStatToObject(JNIEnv *, struct stat, int errnoValue, jobject); /* DO NOT EDIT THIS FILE - it is machine generated */ @@ -119,6 +119,22 @@ JNIEXPORT jbyteArray JNICALL Java_org_eclipse_core_internal_filesystem_local_uni JNIEXPORT jint JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_getflag (JNIEnv *, jclass, jbyteArray); +/* + * Class: org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives + * Method: listDir + * Signature: ([B)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_listDir + (JNIEnv *, jclass, jbyteArray); + +/* + * Class: org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives + * Method: listDirAndGetFileInfos + * Signature: ([B)[Lorg/eclipse/core/internal/filesystem/local/unix/StructStat; + */ +JNIEXPORT jobjectArray JNICALL Java_org_eclipse_core_internal_filesystem_local_unix_UnixFileNatives_listDirAndGetFileInfos + (JNIEnv *, jclass, jbyteArray); + #ifdef __cplusplus } #endif 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..2a7e112254f 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,7 @@ * and is used by JNI calls wrapping OS file related functions. */ public class StructStat { + private static final int ENOENT = 2; // errno value for "No such file or directory" private static final boolean USE_MILLISECOND_RESOLUTION = Boolean.parseBoolean(System.getProperty("eclipse.filesystem.useNatives.modificationTimestampMillisecondsResolution", "true")); //$NON-NLS-1$ //$NON-NLS-2$ @@ -29,10 +31,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 +54,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/UnixFileHandler.java b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileHandler.java index c13ae00f851..2ca8f50269d 100644 --- a/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileHandler.java +++ b/resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/unix/UnixFileHandler.java @@ -21,6 +21,17 @@ * Native handler that delegates to UnixFileNatives */ public class UnixFileHandler extends NativeHandler { + + @Override + public String[] listDirectoryNames(String fileName) { + return UnixFileNatives.listDirectoryNames(fileName); + } + + @Override + public IFileInfo[] listDirectoryAndGetFileInfos(String fileName) { + return UnixFileNatives.listDirectoryAndGetFileInfos(fileName); + } + @Override public int getSupportedAttributes() { return UnixFileNatives.getSupportedAttributes(); 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..b446b41e784 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; @@ -35,6 +37,8 @@ public abstract class UnixFileNatives { private static final boolean usingNatives; private static final int libattr; + protected static final String[] EMPTY_STRING_ARRAY = {}; + static { boolean _usingNatives = false; int _libattr = 0; @@ -76,27 +80,51 @@ public static int getSupportedAttributes() { return ret; } + public static String[] listDirectoryNames(String pathName) { + byte[] name = fileNameToBytes(pathName); + String[] result = listDir(name); + if (result == null) { + return EMPTY_STRING_ARRAY; + } + return result; + } + + public static IFileInfo[] listDirectoryAndGetFileInfos(String pathName) { + byte[] name = fileNameToBytes(pathName); + StructStat[] stats = listDirAndGetFileInfos(name); + if (stats == null) { + return new IFileInfo[0]; + } + int count = stats.length; + IFileInfo[] infos = new IFileInfo[count]; + for (int i = 0; i < count; i++) { + var st = stats[i].toFileInfo(); + infos[i] = st; + } + return infos; + } + 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 { @@ -231,4 +259,8 @@ private static boolean isSupported(int attr) { private static final native int getflag(byte[] buf); + private static final native String[] listDir(byte[] path); + + private static final native StructStat[] listDirAndGetFileInfos(byte[] path); + } From e91395205b31e621742def1b31fd643dec231b77 Mon Sep 17 00:00:00 2001 From: Eclipse Platform Bot Date: Fri, 19 Jun 2026 15:46:35 +0000 Subject: [PATCH 2/2] Version bump(s) for 4.41 stream --- .../META-INF/MANIFEST.MF | 2 +- .../bundles/org.eclipse.core.filesystem.linux.x86_64/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/META-INF/MANIFEST.MF b/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/META-INF/MANIFEST.MF index ada3f4f2eeb..f37a72add76 100644 --- a/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/META-INF/MANIFEST.MF +++ b/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %fragmentName Bundle-SymbolicName: org.eclipse.core.filesystem.linux.x86_64; singleton:=true -Bundle-Version: 1.2.400.qualifier +Bundle-Version: 1.2.500.qualifier Bundle-Vendor: %providerName Fragment-Host: org.eclipse.core.filesystem;bundle-version="[1.7.200,2.0.0)" Bundle-Localization: fragment diff --git a/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/pom.xml b/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/pom.xml index 1d83bb509fd..3141d7d51dd 100644 --- a/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/pom.xml +++ b/resources/bundles/org.eclipse.core.filesystem.linux.x86_64/pom.xml @@ -18,7 +18,7 @@ ../../ org.eclipse.core.filesystem.linux.x86_64 - 1.2.400-SNAPSHOT + 1.2.500-SNAPSHOT eclipse-plugin