mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1819253 - Retry IOUtils.remove on E_ACCESSDENIED r=nika
Differential Revision: https://phabricator.services.mozilla.com/D171183
This commit is contained in:
parent
8b7a8c4b9d
commit
adc87dc487
@ -576,6 +576,14 @@ dictionary RemoveOptions {
|
||||
* If true, and the target is a directory, recursively remove files.
|
||||
*/
|
||||
boolean recursive = false;
|
||||
|
||||
/**
|
||||
* If true, a failed delete on a readonly file will be retried by first
|
||||
* removing the readonly attribute.
|
||||
*
|
||||
* Only has an effect on Windows.
|
||||
*/
|
||||
boolean retryReadonly = false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -665,8 +665,9 @@ already_AddRefed<Promise> IOUtils::Remove(GlobalObject& aGlobal,
|
||||
DispatchAndResolve<Ok>(
|
||||
state->mEventQueue, promise,
|
||||
[file = std::move(file), ignoreAbsent = aOptions.mIgnoreAbsent,
|
||||
recursive = aOptions.mRecursive]() {
|
||||
return RemoveSync(file, ignoreAbsent, recursive);
|
||||
recursive = aOptions.mRecursive,
|
||||
retryReadonly = aOptions.mRetryReadonly]() {
|
||||
return RemoveSync(file, ignoreAbsent, recursive, retryReadonly);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1613,9 +1614,13 @@ Result<Ok, IOUtils::IOError> IOUtils::CopyOrMoveSync(CopyOrMoveFn aMethod,
|
||||
/* static */
|
||||
Result<Ok, IOUtils::IOError> IOUtils::RemoveSync(nsIFile* aFile,
|
||||
bool aIgnoreAbsent,
|
||||
bool aRecursive) {
|
||||
bool aRecursive,
|
||||
bool aRetryReadonly) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
// Prevent an unused variable warning.
|
||||
(void)aRetryReadonly;
|
||||
|
||||
nsresult rv = aFile->Remove(aRecursive);
|
||||
if (aIgnoreAbsent && IsFileNotFound(rv)) {
|
||||
return Ok();
|
||||
@ -1634,6 +1639,17 @@ Result<Ok, IOUtils::IOError> IOUtils::RemoveSync(nsIFile* aFile,
|
||||
"Specify the `recursive: true` option to mitigate this error",
|
||||
aFile->HumanReadablePath().get()));
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
if (rv == NS_ERROR_FILE_ACCESS_DENIED && aRetryReadonly) {
|
||||
MOZ_TRY(SetWindowsAttributesSync(aFile, 0, FILE_ATTRIBUTE_READONLY));
|
||||
return RemoveSync(aFile, aIgnoreAbsent, aRecursive,
|
||||
/* aRetryReadonly = */ false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return Err(err.WithMessage("Could not remove the file at %s",
|
||||
aFile->HumanReadablePath().get()));
|
||||
}
|
||||
|
@ -401,16 +401,20 @@ class IOUtils final {
|
||||
/**
|
||||
* Attempts to remove the file located at |aFile|.
|
||||
*
|
||||
* @param aFile The location of the file.
|
||||
* @param aIgnoreAbsent If true, suppress errors due to an absent target file.
|
||||
* @param aRecursive If true, attempt to recursively remove descendant
|
||||
* files. This option is safe to use even if the target
|
||||
* is not a directory.
|
||||
* @param aFile The location of the file.
|
||||
* @param aIgnoreAbsent If true, suppress errors due to an absent target
|
||||
* file.
|
||||
* @param aRecursive If true, attempt to recursively remove descendant
|
||||
* files. This option is safe to use even if the target
|
||||
* is not a directory.
|
||||
* @param aRetryReadonly Retry a delete that failed with a NotAllowedError by
|
||||
* first removing the readonly attribute. Only has an
|
||||
* effect on Windows.
|
||||
*
|
||||
* @return Ok if the file was removed successfully, or an error.
|
||||
*/
|
||||
static Result<Ok, IOError> RemoveSync(nsIFile* aFile, bool aIgnoreAbsent,
|
||||
bool aRecursive);
|
||||
bool aRecursive, bool aRetryReadonly);
|
||||
|
||||
/**
|
||||
* Attempts to create a new directory at |aFile|.
|
||||
|
@ -78,6 +78,33 @@
|
||||
"IOUtils::remove can recursively remove a directory"
|
||||
);
|
||||
});
|
||||
|
||||
if (Services.appinfo.OS === "WINNT") {
|
||||
add_task(async function test_remove_retry_readonly() {
|
||||
|
||||
const tmpDir = PathUtils.join(PathUtils.tempDir, "test_ioutils_remove_retry_readonly.tmp.d");
|
||||
const path = PathUtils.join(tmpDir, "file.txt");
|
||||
|
||||
await createDir(tmpDir);
|
||||
await createFile(path, "");
|
||||
|
||||
await IOUtils.setWindowsAttributes(path, { readOnly: true });
|
||||
|
||||
await Assert.rejects(
|
||||
IOUtils.remove(path),
|
||||
/NotAllowedError/,
|
||||
"Cannot remove a readonly file by default"
|
||||
);
|
||||
|
||||
Assert.ok(await fileExists(path), "File should still exist");
|
||||
|
||||
await IOUtils.remove(path, { retryReadonly: true });
|
||||
|
||||
Assert.ok(!await fileExists(path), "File should not exist");
|
||||
|
||||
await IOUtils.remove(tmpDir);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user