mirror of
https://github.com/abraunegg/onedrive
synced 2026-03-14 14:35:46 +01:00
Update safeRemove() to retry on EINTR / EBUSY filesystem responses
This change improves the robustness of local file cleanup by enhancing util.safeRemove() to retry deletion when the underlying filesystem operation is interrupted or temporarily busy. What changed safeRemove() now retries remove() when: EINTR — Interrupted system call (signal received before syscall completion) EBUSY — transient “resource busy” conditions Retries are capped and include a small backoff to avoid tight retry loops. Existing behaviour is preserved: ENOENT is treated as success (file already removed). All other error conditions are logged once and returned. Why this is needed Under normal operation (signal handling, network interruptions, shutdown sequences), POSIX systems such as Linux and FreeBSD can legitimately return EINTR for file deletion calls. Treating this as a hard failure creates noisy logs and can leave temporary files behind even though a retry would succeed. In rarer cases, EBUSY may also be returned for transient filesystem conditions. A limited retry avoids false error reporting while still surfacing persistent failures. Scope Applies to Linux and FreeBSD. No change to functional semantics or error visibility for genuine failures. Reduces spurious “Interrupted system call” errors observed in debug logs, particularly for temporary resume download files. This aligns safeRemove() with POSIX-recommended retry behaviour for interruptible system calls while keeping retries bounded and safe.
This commit is contained in:
parent
e7a2cf2fe1
commit
30523670d5
1 changed files with 29 additions and 15 deletions
44
src/util.d
44
src/util.d
|
|
@ -3,7 +3,7 @@ module util;
|
|||
|
||||
// What does this module require to function?
|
||||
import core.memory;
|
||||
import core.stdc.errno : ENOENT;
|
||||
import core.stdc.errno : ENOENT, EINTR, EBUSY;
|
||||
import core.stdc.stdlib;
|
||||
import core.stdc.string;
|
||||
import core.sys.posix.pwd;
|
||||
|
|
@ -208,23 +208,37 @@ void safeRename(const(char)[] oldPath, const(char)[] newPath, bool dryRun) {
|
|||
}
|
||||
}
|
||||
|
||||
// Deletes the specified file without throwing an exception if it does not exists
|
||||
// Deletes the specified file without throwing an exception if there is an issue
|
||||
void safeRemove(const(char)[] path) {
|
||||
// Set this function name
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__) , strip(getFunctionName!({})));
|
||||
|
||||
// Attempt the local deletion
|
||||
try {
|
||||
// Attempt once; no pre-check to avoid TOCTTOU
|
||||
remove(path); // attempt once, no pre-check
|
||||
return; // removed
|
||||
} catch (FileException e) {
|
||||
if (e.errno == ENOENT) { // already gone → fine
|
||||
return; // nothing to do
|
||||
string thisFunctionName = format("%s.%s", strip(__MODULE__), strip(getFunctionName!({})));
|
||||
|
||||
int maxAttempts = 5;
|
||||
|
||||
foreach (attempt; 0 .. maxAttempts) {
|
||||
try {
|
||||
// Attempt to remove; no pre-check to avoid TOCTTOU
|
||||
remove(path);
|
||||
return;
|
||||
} catch (FileException e) {
|
||||
if (e.errno == ENOENT) return; // already gone → fine
|
||||
if (e.errno == EINTR) { // Interrupted by signal → retry
|
||||
// 10ms backoff to avoid spinning if signals are frequent
|
||||
Thread.sleep(dur!"msecs"(10 * (attempt + 1)));
|
||||
continue;
|
||||
}
|
||||
if (e.errno == EBUSY) { // Filesystem was busy → retry
|
||||
// 25ms backoff to avoid spinning if signals are frequent
|
||||
Thread.sleep(dur!"msecs"(25 * (attempt + 1)));
|
||||
continue;
|
||||
}
|
||||
// Anything else is noteworthy (EISDIR, EACCES, etc.)
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, to!string(path));
|
||||
return;
|
||||
}
|
||||
// Anything else is noteworthy (EISDIR, EACCES, etc.)
|
||||
displayFileSystemErrorMessage(e.msg, thisFunctionName, to!string(path));
|
||||
}
|
||||
// If we get here, we exhausted retries on EINTR
|
||||
// Log the last failure
|
||||
displayFileSystemErrorMessage("Failed to remove file after retries: " ~ to!string(path), thisFunctionName, to!string(path));
|
||||
}
|
||||
|
||||
// Returns the quickXorHash base64 string of a file, or an empty string on failure
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue