Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check preconditions in FileSystemResourceManager.write #103 #393

Merged
merged 2 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1148,21 +1148,58 @@ public void updateLocalSync(ResourceInfo info, long localSyncInfo) {
}

/**
* The target must exist in the workspace. The content InputStream is
* closed even if the method fails. If the force flag is false we only write
* the file if it does not exist or if it is already local and the timestamp
* has NOT changed since last synchronization, otherwise a CoreException
* is thrown.
* The target must exist in the workspace and must remain existing throughout
* the execution of this method. The {@code content} {@link InputStream} is
* closed even if the method fails. If the {@link IResource#FORCE} flag is not
* set in {@code updateFlags}, we only write the file if it does not exist or if
* it is already local and the timestamp has <b>not</b> changed since last
* synchronization, otherwise a {@link CoreException} is thrown.
*
* @param target the file to write to
* @param content a stream with the contents to write to {@code target}
* @param fileInfo the info object for the {@code target} file
* @param updateFlags update flags as defined in {@link IResource}
* @param append whether the {@code content} stream shall be appended to
* the existing contents of {@code target}
* @param monitor the progress monitor to report to
*
* @throws CoreException in any of the following cases:
* <ul>
* <li>the given {@code target} does not exist or was
* removed from the workspace concurrently
* <li>writing the stream to {@code target} fails
* <li>the {@link IResource#FORCE} flag is set in
* {@code updateFlags}, {@code append} is {@code true},
* and the file is not local or does not exist</li>
* <li>the {@link IResource#FORCE} flag is not set in
* {@code updateFlags} and
* <ul>
* <li>{@code target} is local and has been modified since
* last synchronization</li>
* <li>{@code target} is not local but exists or
* {@code append} is {code true}</li>
* </ul>
* </ul>
*
* @see IResource#FORCE
* @see IResource#KEEP_HISTORY
*/
public void write(IFile target, InputStream content, IFileInfo fileInfo, int updateFlags, boolean append, IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 4);
try {
Resource targetResource = (Resource) target;
IFileStore store = getStore(target);
if (fileInfo.getAttribute(EFS.ATTRIBUTE_READ_ONLY)) {
String message = NLS.bind(Messages.localstore_couldNotWriteReadOnly, target.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, target.getFullPath(), message, null);
}
long lastModified = fileInfo.getLastModified();
ResourceInfo immutableTargetResourceInfo = targetResource.getResourceInfo(true, false);
if (immutableTargetResourceInfo == null) {
// If the resource info is null, the resource does not exist in the workspace.
// This violates the method contract, so throw an according exception.
targetResource.checkExists(targetResource.getFlags(immutableTargetResourceInfo), true);
}
if (BitMask.isSet(updateFlags, IResource.FORCE)) {
if (append && !target.isLocal(IResource.DEPTH_ZERO) && !fileInfo.exists()) {
// force=true, local=false, existsInFileSystem=false
Expand All @@ -1171,12 +1208,8 @@ public void write(IFile target, InputStream content, IFileInfo fileInfo, int upd
}
} else {
if (target.isLocal(IResource.DEPTH_ZERO)) {
ResourceInfo info = ((Resource) target).getResourceInfo(true, false);
if (info == null) {
throw new IllegalStateException("No ResourceInfo for: " + target); //$NON-NLS-1$
}
// test if timestamp is the same since last synchronization
if (lastModified != info.getLocalSyncInfo()) {
if (lastModified != immutableTargetResourceInfo.getLocalSyncInfo()) {
asyncRefresh(target);
String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, target.getFullPath());
throw new ResourceException(IResourceStatus.OUT_OF_SYNC_LOCAL, target.getFullPath(), message, null);
Expand Down Expand Up @@ -1235,15 +1268,17 @@ public void write(IFile target, InputStream content, IFileInfo fileInfo, int upd
}
// get the new last modified time and stash in the info
lastModified = store.fetchInfo().getLastModified();
ResourceInfo info = ((Resource) target).getResourceInfo(false, true);
if (info == null) {
// happens see Bug 571133
throw new IllegalStateException("No ResourceInfo for: " + target); //$NON-NLS-1$
ResourceInfo mutableTargetResourceInfo = targetResource.getResourceInfo(false, true);
if (mutableTargetResourceInfo == null) {
// If the resource info is null, the resource must have been concurrently
// removed from the workspace. This violates the method contract, so throw an
// according exception.
targetResource.checkExists(targetResource.getFlags(mutableTargetResourceInfo), true);
}
updateLocalSync(info, lastModified);
info.incrementContentId();
info.clear(M_CONTENT_CACHE);
workspace.updateModificationStamp(info);
updateLocalSync(mutableTargetResourceInfo, lastModified);
mutableTargetResourceInfo.incrementContentId();
mutableTargetResourceInfo.clear(M_CONTENT_CACHE);
workspace.updateModificationStamp(mutableTargetResourceInfo);
} finally {
FileUtil.safeClose(content);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,33 @@ public void testWriteFile() throws CoreException {
/* test the overwrite parameter (false) */
ensureDoesNotExistInFileSystem(file); // FIXME Race Condition with asynchronous workplace refresh see Bug 571133
InputStream another3 = getContents(anotherContent);
waitForRefresh(); // wait for refresh to ensure that file is not present in workspace
assertThrows("Should fail writing non existing file", CoreException.class,
() -> write(file, another3, false, null));

/* remove trash */
ensureDoesNotExistInWorkspace(project);
}

// See https://github.com/eclipse-platform/eclipse.platform/issues/103
@Test
public void testWriteFile2() {
public void testWriteDeletedFile() throws CoreException {
/* initialize common objects */
IProject project = projects[0];
IFile file = project.getFile("testWriteFile");
ensureExistsInWorkspace(file, true);
String content = "original";

/* write file for the first time */
write(file, getContents(content), true, null);

file.delete(true, null);
assertThrows("Should fail writing file that is already deleted", CoreException.class,
() -> write(file, getContents(content), false, null));
}

@Test
public void testWriteFileNotInWorkspace() {
// Bug 571133
IProject project = projects[0];
IFile file = project.getFile("testWriteFile2");
Expand All @@ -316,7 +334,7 @@ public void testWriteFile2() {
/* common contents */
String anotherContent = "and this string should not... well, you know...";
InputStream another = getContents(anotherContent);
assertThrows(IllegalStateException.class, () -> write(file, another, false, null));
assertThrows(CoreException.class, () -> write(file, another, false, null));

/* remove trash */
ensureDoesNotExistInWorkspace(project);
Expand Down Expand Up @@ -417,7 +435,7 @@ protected void write(final IFile file, final InputStream contents, final boolean
try {
IWorkspace workspace = getWorkspace();
assertNotNull("workspace cannot be null", workspace);
workspace.run(new WriteFileContents(file, contents, force, getLocalManager()), null);
workspace.run(new WriteFileContents(file, contents, force, getLocalManager()), monitor);
} catch (Throwable t) {
// Bug 541493: we see unlikely stack traces reported by JUnit here, log the
// exceptions in case JUnit filters stack frames
Expand Down
Loading