Skip to content

Commit 4fda918

Browse files
committed
[FS] Always copy file-attributes using Eclipse's Native(File)Handler
Files.copy seems to copy file-attributes differently than Eclipse NativeHandler (for files) do on Mac. In order to stay consistent always use the Eclipse way. Fixes #1504
1 parent 7fc0709 commit 4fda918

File tree

7 files changed

+110
-109
lines changed

7 files changed

+110
-109
lines changed

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2015 IBM Corporation and others.
2+
* Copyright (c) 2005, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -25,6 +25,7 @@
2525
import org.eclipse.core.internal.filesystem.FileCache;
2626
import org.eclipse.core.internal.filesystem.Messages;
2727
import org.eclipse.core.internal.filesystem.Policy;
28+
import org.eclipse.core.internal.filesystem.local.LocalFile;
2829
import org.eclipse.core.runtime.CoreException;
2930
import org.eclipse.core.runtime.IPath;
3031
import org.eclipse.core.runtime.IProgressMonitor;
@@ -129,7 +130,7 @@ protected void copyDirectory(IFileInfo sourceInfo, IFileStore destination, int o
129130
// create directory
130131
destination.mkdir(EFS.NONE, subMonitor.newChild(1));
131132
// copy attributes
132-
transferAttributes(sourceInfo, destination);
133+
LocalFile.transferAttributes(sourceInfo, destination);
133134

134135
if (children == null)
135136
return;
@@ -167,7 +168,7 @@ protected void copyFile(IFileInfo sourceInfo, IFileStore destination, int option
167168
OutputStream out = destination.openOutputStream(EFS.NONE, subMonitor.newChild(1));) {
168169
in.transferTo(out);
169170
subMonitor.worked(93);
170-
transferAttributes(sourceInfo, destination);
171+
LocalFile.transferAttributes(sourceInfo, destination);
171172
subMonitor.worked(5);
172173
} catch (IOException e) {
173174
Policy.error(EFS.ERROR_WRITE, NLS.bind(Messages.failedCopy, sourcePath), e);
@@ -429,8 +430,4 @@ public String toString() {
429430
@Override
430431
public abstract URI toURI();
431432

432-
private void transferAttributes(IFileInfo sourceInfo, IFileStore destination) throws CoreException {
433-
int options = EFS.SET_ATTRIBUTES | EFS.SET_LAST_MODIFIED;
434-
destination.putInfo(sourceInfo, options, null);
435-
}
436433
}

resources/bundles/org.eclipse.core.filesystem/src/org/eclipse/core/internal/filesystem/local/LocalFile.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2016 IBM Corporation and others.
2+
* Copyright (c) 2005, 2024 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -148,28 +148,37 @@ public void copy(IFileStore destFile, int options, IProgressMonitor monitor) thr
148148
super.copy(destFile, options, monitor);
149149
}
150150

151-
private static final CopyOption[] NO_OVERWRITE = {StandardCopyOption.COPY_ATTRIBUTES};
152-
private static final CopyOption[] OVERWRITE_EXISTING = {StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING};
153-
private static final int LARGE_FILE_SIZE_THRESHOLD = 1024 * 1024; // 1 MiB experimentally determined
151+
private static final CopyOption[] NO_OVERWRITE = {};
152+
private static final CopyOption[] OVERWRITE_EXISTING = {StandardCopyOption.REPLACE_EXISTING};
153+
public static final int LARGE_FILE_SIZE_THRESHOLD = 1024 * 1024; // 1 MiB experimentally determined
154154

155155
@Override
156156
protected void copyFile(IFileInfo sourceInfo, IFileStore destination, int options, IProgressMonitor monitor) throws CoreException {
157157
if (sourceInfo.getLength() > LARGE_FILE_SIZE_THRESHOLD && destination instanceof LocalFile target) {
158+
SubMonitor subMonitor = SubMonitor.convert(monitor, NLS.bind(Messages.copying, this), 100);
158159
try {
159160
boolean overwrite = (options & EFS.OVERWRITE) != 0;
160161
Files.copy(this.file.toPath(), target.file.toPath(), overwrite ? OVERWRITE_EXISTING : NO_OVERWRITE);
162+
subMonitor.worked(93);
163+
transferAttributes(sourceInfo, destination);
164+
subMonitor.worked(5);
161165
} catch (FileAlreadyExistsException e) {
162166
Policy.error(EFS.ERROR_EXISTS, NLS.bind(Messages.fileExists, target.filePath), e);
163167
} catch (IOException e) {
164168
Policy.error(EFS.ERROR_WRITE, NLS.bind(Messages.failedCopy, this.filePath, target.filePath), e);
165169
} finally {
166-
IProgressMonitor.done(monitor);
170+
subMonitor.done();
167171
}
168172
} else {
169173
super.copyFile(sourceInfo, destination, options, monitor);
170174
}
171175
}
172176

177+
public static final void transferAttributes(IFileInfo sourceInfo, IFileStore destination) throws CoreException {
178+
int options = EFS.SET_ATTRIBUTES | EFS.SET_LAST_MODIFIED;
179+
destination.putInfo(sourceInfo, options, null);
180+
}
181+
173182
@Override
174183
public void delete(int options, IProgressMonitor monitor) throws CoreException {
175184
if (monitor == null)

resources/tests/org.eclipse.core.tests.resources/META-INF/MANIFEST.MF

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Import-Package: org.assertj.core.api,
4040
org.junit.jupiter.api.extension,
4141
org.junit.jupiter.api.function,
4242
org.junit.jupiter.api.io,
43+
org.junit.jupiter.params,
44+
org.junit.jupiter.params.provider,
4345
org.junit.platform.suite.api,
4446
org.mockito
4547
Bundle-ActivationPolicy: lazy

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

Lines changed: 44 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
*******************************************************************************/
1414
package org.eclipse.core.tests.filesystem;
1515

16-
import static org.assertj.core.api.Assertions.assertThat;
1716
import static org.eclipse.core.tests.internal.localstore.LocalStoreTestUtil.createTree;
1817
import static org.eclipse.core.tests.internal.localstore.LocalStoreTestUtil.getTree;
1918
import static org.eclipse.core.tests.resources.ResourceTestUtil.createInFileSystem;
20-
import static org.eclipse.core.tests.resources.ResourceTestUtil.createInputStream;
2119
import static org.eclipse.core.tests.resources.ResourceTestUtil.createRandomString;
2220
import static org.eclipse.core.tests.resources.ResourceTestUtil.createTestMonitor;
2321
import static org.eclipse.core.tests.resources.ResourceTestUtil.getFileStore;
@@ -34,14 +32,15 @@
3432

3533
import java.io.File;
3634
import java.io.IOException;
37-
import java.io.InputStream;
3835
import java.io.OutputStream;
3936
import java.net.URI;
4037
import java.nio.file.Files;
4138
import java.nio.file.Path;
4239
import java.util.ArrayList;
40+
import java.util.Arrays;
4341
import java.util.Collections;
4442
import java.util.List;
43+
import java.util.Random;
4544
import java.util.concurrent.atomic.AtomicInteger;
4645
import java.util.stream.Stream;
4746
import org.eclipse.core.filesystem.EFS;
@@ -56,20 +55,23 @@
5655
import org.eclipse.core.resources.IResource;
5756
import org.eclipse.core.runtime.CoreException;
5857
import org.eclipse.core.runtime.IPath;
59-
import org.eclipse.core.runtime.IProgressMonitor;
6058
import org.eclipse.core.runtime.Platform;
6159
import org.eclipse.core.tests.resources.util.WorkspaceResetExtension;
6260
import org.eclipse.osgi.util.NLS;
6361
import org.junit.jupiter.api.Test;
6462
import org.junit.jupiter.api.extension.ExtendWith;
6563
import org.junit.jupiter.api.io.TempDir;
64+
import org.junit.jupiter.params.ParameterizedTest;
65+
import org.junit.jupiter.params.provider.ValueSource;
6666

6767
/**
6868
* Basic tests for the IFileStore API
6969
*/
7070
@ExtendWith(WorkspaceResetExtension.class)
7171
public class FileStoreTest {
7272

73+
private static final Random RANDOM = new Random();
74+
7375
private IFileStore createDir(IFileStore store, boolean clear) throws CoreException {
7476
if (clear && store.fetchInfo().exists()) {
7577
store.delete(EFS.NONE, null);
@@ -93,10 +95,12 @@ private static IFileStore randomUniqueNotExistingFileStore() throws IOException
9395
return getFileStore(randomUniqueNotExistingPath());
9496
}
9597

96-
private void createFile(IFileStore target, String content) throws CoreException, IOException {
97-
try (OutputStream output = target.openOutputStream(EFS.NONE, null)) {
98-
createInputStream(content).transferTo(output);
99-
}
98+
private void createFile(IFileStore target, String content) throws CoreException {
99+
createFile(target, content.getBytes());
100+
}
101+
102+
private void createFile(IFileStore target, byte[] content) throws CoreException {
103+
target.write(content, EFS.NONE, null);
100104
}
101105

102106
/**
@@ -146,9 +150,9 @@ private IFileStore[] getFileStoresOnTwoVolumes() {
146150
/**
147151
* Basically this is a test for the Windows Platform.
148152
*/
149-
150-
@Test
151-
public void testCopyAcrossVolumes() throws Throwable {
153+
@ParameterizedTest(name = "File size {0} bytes")
154+
@ValueSource(ints = { 10, LocalFile.LARGE_FILE_SIZE_THRESHOLD + 10 })
155+
public void testCopyAcrossVolumes(int fileSize) throws Throwable {
152156
IFileStore[] tempDirectories = getFileStoresOnTwoVolumes();
153157

154158
/* test if we are in the adequate environment */
@@ -166,7 +170,7 @@ public void testCopyAcrossVolumes() throws Throwable {
166170

167171
IFileStore target = tempSrc.getChild(subfolderName);
168172
createDir(target, true);
169-
createTree(getTree(target));
173+
createTree(getTree(target), fileSize);
170174

171175
/* c:\temp\target -> d:\temp\target */
172176
IFileStore destination = tempDest.getChild(subfolderName);
@@ -228,34 +232,31 @@ public void testCopyDirectoryParentMissing() throws Throwable {
228232
assertTrue(!child.fetchInfo().exists());
229233
}
230234

231-
@Test
232-
public void testCaseInsensitive() throws Throwable {
233-
IFileStore temp = createDir(randomUniqueNotExistingFileStore(), true);
235+
@ParameterizedTest(name = "File size {0} bytes")
236+
@ValueSource(ints = { 10, LocalFile.LARGE_FILE_SIZE_THRESHOLD + 10 })
237+
public void testCaseInsensitive(int fileSize) throws Throwable {
238+
IFileStore temp = createDir(randomUniqueNotExistingFileStore(), true);
234239
boolean isCaseSensitive = temp.getFileSystem().isCaseSensitive();
235240
assumeFalse("only relevant for platforms with case-sensitive file system", isCaseSensitive);
236241

242+
byte[] content = new byte[fileSize];
243+
RANDOM.nextBytes(content);
244+
237245
// create a file
238-
String content = "this is just a simple content \n to a simple file \n to test a 'simple' copy";
239246
IFileStore fileWithSmallName = temp.getChild("filename");
240247
fileWithSmallName.delete(EFS.NONE, null);
241248
createFile(fileWithSmallName, content);
242249
System.out.println(fileWithSmallName.fetchInfo().getName());
243250
assertTrue(fileWithSmallName.fetchInfo().exists());
244-
try (InputStream stream = fileWithSmallName.openInputStream(EFS.NONE, null)) {
245-
assertThat(stream).hasContent(content);
246-
}
251+
assertTrue( Arrays.equals(content, fileWithSmallName.readAllBytes(EFS.NONE, null)));
247252

248253
IFileStore fileWithOtherName = temp.getChild("FILENAME");
249254
System.out.println(fileWithOtherName.fetchInfo().getName());
250255
// file content is already the same for both Cases:
251-
try (InputStream stream = fileWithOtherName.openInputStream(EFS.NONE, null)) {
252-
assertThat(stream).hasContent(content);
253-
}
256+
assertTrue(Arrays.equals(content, fileWithOtherName.readAllBytes(EFS.NONE, null)));
254257
fileWithSmallName.copy(fileWithOtherName, IResource.DEPTH_INFINITE, null); // a NOP Operation
255258
// file content is still the same for both Cases:
256-
try (InputStream stream = fileWithOtherName.openInputStream(EFS.NONE, null)) {
257-
assertThat(stream).hasContent(content);
258-
}
259+
assertTrue(Arrays.equals(content, fileWithOtherName.readAllBytes(EFS.NONE, null)));
259260
assertTrue(fileWithOtherName.fetchInfo().exists());
260261
assertTrue(fileWithSmallName.fetchInfo().exists());
261262
fileWithOtherName.delete(EFS.NONE, null);
@@ -267,26 +268,25 @@ public void testCaseInsensitive() throws Throwable {
267268
assertEquals(message, exception.getMessage());
268269
}
269270

270-
@Test
271-
public void testCopyFile() throws Throwable {
271+
@ParameterizedTest(name = "File size {0} bytes")
272+
@ValueSource(ints = { 10, LocalFile.LARGE_FILE_SIZE_THRESHOLD + 10 })
273+
public void testCopyFile(int fileSize) throws Throwable {
272274
/* build scenario */
273275
IFileStore temp = createDir(randomUniqueNotExistingFileStore(), true);
276+
byte[] content = new byte[fileSize];
277+
RANDOM.nextBytes(content);
278+
274279
// create target
275-
String content = "this is just a simple content \n to a simple file \n to test a 'simple' copy";
276280
IFileStore target = temp.getChild("target");
277281
target.delete(EFS.NONE, null);
278282
createFile(target, content);
279283
assertTrue(target.fetchInfo().exists());
280-
try (InputStream stream = target.openInputStream(EFS.NONE, null)) {
281-
assertThat(stream).hasContent(content);
282-
}
284+
assertTrue(Arrays.equals(content, target.readAllBytes(EFS.NONE, null)));
283285

284286
/* temp\target -> temp\copy of target */
285287
IFileStore copyOfTarget = temp.getChild("copy of target");
286288
target.copy(copyOfTarget, IResource.DEPTH_INFINITE, null);
287-
try (InputStream stream = copyOfTarget.openInputStream(EFS.NONE, null)) {
288-
assertThat(stream).hasContent(content);
289-
}
289+
assertTrue(Arrays.equals(content, copyOfTarget.readAllBytes(EFS.NONE, null)));
290290
copyOfTarget.delete(EFS.NONE, null);
291291

292292
// We need to know whether or not we can unset the read-only flag
@@ -297,43 +297,22 @@ public void testCopyFile() throws Throwable {
297297
setReadOnly(target, true);
298298

299299
target.copy(copyOfTarget, IResource.DEPTH_INFINITE, null);
300-
try (InputStream stream = copyOfTarget.openInputStream(EFS.NONE, null)) {
301-
assertThat(stream).hasContent(content);
302-
}
300+
assertTrue(Arrays.equals(content, copyOfTarget.readAllBytes(EFS.NONE, null)));
303301
// reset read only flag for cleanup
304302
setReadOnly(copyOfTarget, false);
305303
copyOfTarget.delete(EFS.NONE, null);
306304
// reset the read only flag for cleanup
307305
setReadOnly(target, false);
308306
target.delete(EFS.NONE, null);
309307
}
310-
311-
/* copy a big file to test progress monitor */
312-
StringBuilder sb = new StringBuilder();
313-
for (int i = 0; i < 1000; i++) {
314-
sb.append("asdjhasldhaslkfjhasldkfjhasdlkfjhasdlfkjhasdflkjhsdaf");
315-
}
316-
IFileStore bigFile = temp.getChild("bigFile");
317-
createFile(bigFile, sb.toString());
318-
assertTrue(bigFile.fetchInfo().exists());
319-
try (InputStream stream = bigFile.openInputStream(EFS.NONE, null)) {
320-
assertThat(stream).hasContent(sb.toString());
321-
}
322-
IFileStore destination = temp.getChild("copy of bigFile");
323-
// IProgressMonitor monitor = new LoggingProgressMonitor(System.out);
324-
IProgressMonitor monitor = createTestMonitor();
325-
bigFile.copy(destination, EFS.NONE, monitor);
326-
try (InputStream stream = destination.openInputStream(EFS.NONE, null)) {
327-
assertThat(stream).hasContent(sb.toString());
328-
}
329-
destination.delete(EFS.NONE, null);
330308
}
331309

332310
/**
333311
* Basically this is a test for the Windows Platform.
334312
*/
335-
@Test
336-
public void testCopyFileAcrossVolumes() throws Throwable {
313+
@ParameterizedTest(name = "File size {0} bytes")
314+
@ValueSource(ints = { 10, LocalFile.LARGE_FILE_SIZE_THRESHOLD + 10 })
315+
public void testCopyFileAcrossVolumes(int fileSize) throws Throwable {
337316
IFileStore[] tempDirectories = getFileStoresOnTwoVolumes();
338317

339318
/* test if we are in the adequate environment */
@@ -346,32 +325,27 @@ public void testCopyFileAcrossVolumes() throws Throwable {
346325
/* get the destination folder */
347326
IFileStore tempDest = tempDirectories[1];
348327
// create target
349-
String content = "this is just a simple content \n to a simple file \n to test a 'simple' copy";
328+
byte[] content = new byte[fileSize];
329+
RANDOM.nextBytes(content);
350330
String subfolderName = "target_" + System.currentTimeMillis();
351331

352332
IFileStore target = tempSrc.getChild(subfolderName);
353333
target.delete(EFS.NONE, null);
354334
createFile(target, content);
355335
assertTrue(target.fetchInfo().exists());
356-
try (InputStream stream = target.openInputStream(EFS.NONE, null)) {
357-
assertThat(stream).hasContent(content);
358-
}
336+
assertTrue(Arrays.equals(content, target.readAllBytes(EFS.NONE, null)));
359337

360338
/* c:\temp\target -> d:\temp\target */
361339
IFileStore destination = tempDest.getChild(subfolderName);
362340
target.copy(destination, IResource.DEPTH_INFINITE, null);
363-
try (InputStream stream = destination.openInputStream(EFS.NONE, null)) {
364-
assertThat(stream).hasContent(content);
365-
}
341+
assertTrue(Arrays.equals(content, destination.readAllBytes(EFS.NONE, null)));
366342
destination.delete(EFS.NONE, null);
367343

368344
/* c:\temp\target -> d:\temp\copy of target */
369345
String copyOfSubfoldername = "copy of " + subfolderName;
370346
destination = tempDest.getChild(copyOfSubfoldername);
371347
target.copy(destination, IResource.DEPTH_INFINITE, null);
372-
try (InputStream stream = destination.openInputStream(EFS.NONE, null)) {
373-
assertThat(stream).hasContent(content);
374-
}
348+
assertTrue(Arrays.equals(content, destination.readAllBytes(EFS.NONE, null)));
375349
destination.delete(EFS.NONE, null);
376350

377351
/* c:\temp\target -> d:\temp\target (but the destination is already a file */
@@ -380,9 +354,7 @@ public void testCopyFileAcrossVolumes() throws Throwable {
380354
createFile(destination, anotherContent);
381355
assertTrue(!destination.fetchInfo().isDirectory());
382356
target.copy(destination, IResource.DEPTH_INFINITE, null);
383-
try (InputStream stream = destination.openInputStream(EFS.NONE, null)) {
384-
assertThat(stream).hasContent(content);
385-
}
357+
assertTrue(Arrays.equals(content, destination.readAllBytes(EFS.NONE, null)));
386358
destination.delete(EFS.NONE, null);
387359

388360
/* c:\temp\target -> d:\temp\target (but the destination is already a folder */

0 commit comments

Comments
 (0)