Skip to content

Commit df26875

Browse files
Michael5601HannesWell
authored andcommitted
Ensure file-attributes are transfered after the file is written
This commit refactors the code by moving the transferAttributes method call outside of the try-with-resources block. Closing the OutputStream prematurely could result in the destination FileStore not creating the file before the file attributes are transferred. Additionally, transferring attributes does not require the streams to remain open. A TestFileStore and a matching test is provided. This commit is an adjustment to #1475. Fixes #1524
1 parent 4fda918 commit df26875

File tree

3 files changed

+133
-4
lines changed

3 files changed

+133
-4
lines changed

resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/filesystem/provider/FileStore.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,12 @@ protected void copyFile(IFileInfo sourceInfo, IFileStore destination, int option
164164
}
165165
String sourcePath = toString();
166166
SubMonitor subMonitor = SubMonitor.convert(monitor, NLS.bind(Messages.copying, sourcePath), 100);
167-
try (InputStream in = openInputStream(EFS.NONE, subMonitor.newChild(1)); //
168-
OutputStream out = destination.openOutputStream(EFS.NONE, subMonitor.newChild(1));) {
169-
in.transferTo(out);
170-
subMonitor.worked(93);
167+
try {
168+
try (InputStream in = openInputStream(EFS.NONE, subMonitor.newChild(1)); //
169+
OutputStream out = destination.openOutputStream(EFS.NONE, subMonitor.newChild(1));) {
170+
in.transferTo(out);
171+
subMonitor.worked(93);
172+
} // Close the streams to ensure the target file exists before transferring attributes
171173
LocalFile.transferAttributes(sourceInfo, destination);
172174
subMonitor.worked(5);
173175
} catch (IOException e) {

resources/tests/org.eclipse.core.tests.resources/src/org/eclipse/core/tests/filesystem/FileStoreTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.eclipse.core.resources.IResource;
5656
import org.eclipse.core.runtime.CoreException;
5757
import org.eclipse.core.runtime.IPath;
58+
import org.eclipse.core.runtime.NullProgressMonitor;
5859
import org.eclipse.core.runtime.Platform;
5960
import org.eclipse.core.tests.resources.util.WorkspaceResetExtension;
6061
import org.eclipse.osgi.util.NLS;
@@ -370,6 +371,23 @@ public void testCopyFileAcrossVolumes(int fileSize) throws Throwable {
370371
destination.delete(EFS.NONE, null);
371372
}
372373

374+
@Test
375+
public void testFileAttributeCopyForSmallFiles() throws CoreException, IOException {
376+
Path root = Files.createDirectories(randomUniqueNotExistingPath());
377+
Path sourceFile = root.resolve("source.txt");
378+
Files.writeString(sourceFile, "This is a test file.");
379+
Path targetFile = root.resolve("target.txt");
380+
381+
IFileStore sourceStore = EFS.getLocalFileSystem().getStore(IPath.fromPath(sourceFile));
382+
IFileStore targetStore = new OnCloseWritingFileStore(targetFile);
383+
384+
sourceStore.copy(targetStore, EFS.NONE, new NullProgressMonitor());
385+
386+
String sourceContent = Files.readString(sourceFile);
387+
String targetContent = Files.readString(targetFile);
388+
assertEquals(sourceContent, targetContent);
389+
}
390+
373391
@Test
374392
public void testGetLength() throws Exception {
375393
// evaluate test environment
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024, 2024 Vector Informatik GmbH and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License v2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
* Contributors:
11+
* Vector Informatik GmbH - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.core.tests.filesystem;
14+
15+
import java.io.ByteArrayOutputStream;
16+
import java.io.IOException;
17+
import java.io.InputStream;
18+
import java.io.OutputStream;
19+
import java.net.URI;
20+
import java.nio.file.Files;
21+
import java.nio.file.LinkOption;
22+
import java.nio.file.Path;
23+
import java.nio.file.attribute.FileTime;
24+
import org.eclipse.core.filesystem.EFS;
25+
import org.eclipse.core.filesystem.IFileInfo;
26+
import org.eclipse.core.filesystem.IFileStore;
27+
import org.eclipse.core.filesystem.provider.FileInfo;
28+
import org.eclipse.core.filesystem.provider.FileStore;
29+
import org.eclipse.core.runtime.CoreException;
30+
import org.eclipse.core.runtime.IProgressMonitor;
31+
import org.eclipse.core.runtime.IStatus;
32+
import org.eclipse.core.runtime.Status;
33+
34+
class OnCloseWritingFileStore extends FileStore {
35+
36+
private final Path filePath;
37+
38+
public OnCloseWritingFileStore(Path file) {
39+
this.filePath = file;
40+
}
41+
42+
@Override
43+
public OutputStream openOutputStream(int options, IProgressMonitor monitor) {
44+
return new ByteArrayOutputStream() {
45+
@Override
46+
public void close() throws IOException {
47+
Files.write(filePath, this.toByteArray());
48+
}
49+
};
50+
}
51+
52+
@Override
53+
public void putInfo(IFileInfo info, int options, IProgressMonitor monitor) throws CoreException {
54+
if ((options & EFS.SET_LAST_MODIFIED) != 0) {
55+
FileTime lastModified = FileTime.fromMillis(info.getLastModified());
56+
try {
57+
Files.setLastModifiedTime(filePath, lastModified);
58+
} catch (IOException e) {
59+
if (!Files.exists(filePath, LinkOption.NOFOLLOW_LINKS)) {
60+
throw new CoreException(new Status(IStatus.ERROR, "CopyBugFileStore", "File does not exist", e));
61+
}
62+
throw new CoreException(new Status(IStatus.ERROR, "CopyBugFileStore", "Failed to set attribute", e));
63+
}
64+
}
65+
}
66+
67+
@Override
68+
public IFileInfo fetchInfo(int options, IProgressMonitor monitor) throws CoreException {
69+
FileInfo info = new FileInfo();
70+
try {
71+
info.setLastModified(Files.getLastModifiedTime(filePath).toMillis());
72+
info.setLength(Files.size(filePath));
73+
info.setExists(Files.exists(filePath));
74+
} catch (IOException e) {
75+
throw new CoreException(new Status(IStatus.ERROR, "TestFileStore", "Failed to fetch file info", e));
76+
}
77+
return info;
78+
}
79+
80+
@Override
81+
public String getName() {
82+
return filePath.getFileName().toString();
83+
}
84+
85+
@Override
86+
public IFileStore getParent() {
87+
throw new UnsupportedOperationException();
88+
}
89+
90+
@Override
91+
public String[] childNames(int options, IProgressMonitor monitor) throws CoreException {
92+
throw new UnsupportedOperationException();
93+
}
94+
95+
@Override
96+
public IFileStore getChild(String name) {
97+
throw new UnsupportedOperationException();
98+
}
99+
100+
@Override
101+
public InputStream openInputStream(int options, IProgressMonitor monitor) throws CoreException {
102+
throw new UnsupportedOperationException();
103+
}
104+
105+
@Override
106+
public URI toURI() {
107+
throw new UnsupportedOperationException();
108+
}
109+
}

0 commit comments

Comments
 (0)