[Win32] Folder becomes a file in the workspace tree when a child is deleted during `refreshLocal` (regression in 2026-09)
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 15/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Estancado
- Stack tecnológico
- java
- Área
- operating-systems
Línea de trabajo
Start with Win32Handler.fetchFileInfo and the listDirectoryAndGetFileInfos override, then LocalFile.childInfos and LocalFile.fetchInfo, to see where the name is left unset when a child disappears. Pull request #3025 already targets this, so review it before touching code. Done means the race test RefreshRaceTest no longer yields an empty-name entry and refreshLocal keeps the folder a folder.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Description
Since Eclipse 2026-09 (org.eclipse.core.filesystem 1.11.500), IResource#refreshLocal can turn an existing folder into an IFile in the workspace tree. This happens when one of the folder's children is deleted while the refresh is listing it. On disk the folder is still a directory, but the workspace treats it as a file. After that, creating anything below it fails:
org.eclipse.core.internal.resources.ResourceException: Resource '/P/work' does not exist.
at org.eclipse.core.internal.resources.Resource.checkExists
at org.eclipse.core.internal.resources.Folder.assertCreateRequirements
at org.eclipse.core.internal.resources.Folder.create
If the race happens directly below the project, AssertionFailedException: Path must include project and resource name is thrown instead.
With auto-refresh enabled, the refresh monitor usually turns the file back into a folder (fileToFolder) a little later. That makes the failure flaky.
Root cause
LocalFile.childInfos()now goes throughLocalFileNativesManager.listDirectoryAndGetFileInfos→NativeHandler.listDirectoryAndGetFileInfos(introduced in #2787). It gets the names fromFile.list()and then callsfetchFileInfo(dir + File.separator + name)for each one.- If a child is deleted between
File.list()and the native lookup,Win32Handler.fetchFileInfo(FindFirstFileW→ERROR_FILE_NOT_FOUND) returns aFileInfowith an empty name,exists=falseand no error code. Unlike the old code path (FileStore.childInfos→LocalFile.fetchInfo), the name is never filled in. - During
refreshLocal:UnifiedTree.getLocalListsorts the""entry first.createChildNodeFromFileSystemappends""to the parent path, which produces a file node at the folder's own path.RefreshLocalVisitor.synchronizeExistencesees a gender variant, andsynchronizeGendercallsfolderToFile.
The Win32Handler override of listDirectoryAndGetFileInfos added in #2952 has the same flaw: it also leaves the name unset on NoSuchFileException.
Suggested fix
The other implementations of NativeHandler.fetchFileInfo(String) all seem to ensure that the returned fileinfo has a filename set, only the Win32Handler does not seem to have such a case and relies on the callers of fetchFileInfo (e.g. LocalFile.fetchInfo(int, IProgressMonitor) to fix this. So maybe we should also make sure the Win32Handler always sets name?
Steps to reproduce
Windows only. The following JUnit 5 test fails reliably within the first few attempts. Test 1 checks the filesystem layer only. Test 2 shows the symptom through the Workspace API.
package org.eclipse.core.tests.filesystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
class RefreshRaceTest {
@TempDir
Path tempDir;
IProject project;
@AfterEach
void afterEach() throws Exception {
if (project != null) {
project.delete(true, true, null);
setAutoRefresh(true);
}
}
@Test
void childInfosNeverReturnsNamelessEntry() throws Exception {
IFileStore store = EFS.getLocalFileSystem().getStore(tempDir.toUri());
race(tempDir, () -> {
for (IFileInfo info : store.childInfos(EFS.NONE, null)) {
Assertions.assertFalse(info.getName().isEmpty(), "entry with empty name: " + info);
}
});
}
@Test
void refreshLocalKeepsFolderAFolder() throws Exception {
setAutoRefresh(false); // the refresh monitor would undo the damage
project = ResourcesPlugin.getWorkspace().getRoot().getProject("P");
project.create(null);
project.open(null);
IFolder folder = project.getFolder("work");
folder.create(true, true, null);
race(folder.getLocation().toPath(), () -> {
project.refreshLocal(IResource.DEPTH_INFINITE, null);
Assertions.assertInstanceOf(IFolder.class, project.findMember("work"));
});
folder.getFolder("data").create(true, true, null);
}
/** Fills dir with files and runs probe while another thread deletes them. */
static void race(Path dir, Probe probe) throws Exception {
for (int attempt = 0; attempt < 20; attempt++) {
List<Path> files = new ArrayList<>();
for (int i = 0; i < 2000; i++) {
files.add(Files.createFile(dir.resolve("file" + i + ".tmp")));
}
Thread deleter = new Thread(() -> files.forEach(f -> f.toFile().delete()));
deleter.start();
while (deleter.isAlive()) {
probe.run();
}
deleter.join();
}
}
static void setAutoRefresh(boolean value) throws Exception {
IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(ResourcesPlugin.PI_RESOURCES);
prefs.putBoolean(ResourcesPlugin.PREF_AUTO_REFRESH, value);
prefs.flush();
}
interface Probe {
void run() throws CoreException;
}
}
Failures observed:
entry with empty name: FileInfo [name=, length=0, lastModified=0, exists=false, attributes=12582912, errorCode=0]
expected: <org.eclipse.core.resources.IFolder> but was: <org.eclipse.core.internal.resources.File>
Disclaimer: I had Claude write this up but debugged most of it manually.
Overall I think that the temporary corruption of the resource tree is a quite a nasty bug.
/cc @HannesWell @iloveeclipse
- Lenguaje dominante
- Java
- Estrellas
- 165
- Forks
- 174
- Merge medio
- 2 d 16 h
- PR fusionados (30 d)
- 42
Preparar el entorno
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de eclipse-platform/eclipse.platform
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
eclipse-platform/eclipse.platform#3019 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
eclipse-platform/eclipse.platform#2940 ·
Los mantenedores suelen responder en 1 día
-
Terminal views all show same name 'Terminal' in Ctrl+F7 view switcher popupPosiblemente ocupada @ravnskjaer la tomó hace 7 días. Abiertobug good first issue help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
eclipse-platform/eclipse.platform#2774 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
eclipse-platform/eclipse.platform#2593 · 7 comentarios ·
Los mantenedores suelen responder en 1 día
-
JGit runtime logs are spamming debugger consolePosiblemente ocupada @HannesWell la tomó hace 1 día. Abiertobug regression
Dificultad 3/5 1-2 días Aptitud para principiantes 56/100
eclipse-platform/eclipse.platform#3022 · 17 comentarios ·
Los mantenedores suelen responder en 1 día
Todos los issues de eclipse-platform/eclipse.platform
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
apache/rocketmq-dashboard#6872 ·
Los mantenedores suelen responder en 3 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
ADORSYS-GIS/token-status-link#168 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
ADORSYS-GIS/keycloak-oid4vp-plugin#232 ·
Los mantenedores suelen responder en 2 días
-
Wrong link to test resultsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 2 días
-
docs(site): Mermaid edge-label contrast on Architecture and a table header on How it comparesAbiertodocumentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
devops-thiago/ThrillhouseBot#1132 ·
Los mantenedores suelen responder en 1 día