mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 16:56:59 +01:00
fix(fuse): fill an incomplete queued revision's gaps on a write open
A partial write whose base was not in the content cache queued an incomplete revision, and every write open over it was refused with EIO until the drain uploaded it. CI's one-client-lan simulation hit this on seed 1: a create over a file whose partial edit had not drained yet. The write open now copies the blob and fills its gaps from the remote base with Core::fill_gaps, as the drain does, and fails only when that fill fails (B204). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuyxByBpK3fSWtyPkugeHz
This commit is contained in:
4 files changed
+87
-6
No files matched your search
@@ -1267,8 +1267,12 @@ impl ProtonFs {
|
||||
//
|
||||
// Complete pending blobs are the common case and can be copied without
|
||||
// any network access. An incomplete blob still has gaps referring to
|
||||
// the remote base; stacking another write on it cannot be represented
|
||||
// safely by WriteHandle, so fail the open rather than risk corruption.
|
||||
// the remote base, and stacking another write on it cannot be
|
||||
// represented by WriteHandle, so the copy's gaps are filled from that
|
||||
// base first, as the drain fills them before an upload. Only when that
|
||||
// fails (offline, or the base is gone) does the open fail: refusing
|
||||
// every such open failed a create over a file whose partial edit had
|
||||
// not drained yet (`docs/BUGS.md` B204).
|
||||
//
|
||||
// Reading that blob and installing the handle cannot be done under one
|
||||
// lock — the copy is the size of the file — so the base is re-checked at
|
||||
@@ -1302,10 +1306,6 @@ impl ProtonFs {
|
||||
}
|
||||
}
|
||||
let pending_base = self.core.pending.lock().get(&uid).cloned();
|
||||
if pending_base.as_ref().is_some_and(|p| !p.meta.complete) {
|
||||
error!(%uid, "refusing write over incomplete queued revision");
|
||||
break 'install Err(Errno::EIO);
|
||||
}
|
||||
let (file, path) = match self.core.cache.create_scratch() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
@@ -1325,6 +1325,25 @@ impl ProtonFs {
|
||||
"copy queued revision into write scratch failed");
|
||||
break 'install Err(Errno::EIO);
|
||||
}
|
||||
if !pending.meta.complete {
|
||||
let mut authored = Intervals::default();
|
||||
for &(s, e) in &pending.meta.authored {
|
||||
authored.add(s, e);
|
||||
}
|
||||
if let Err(error) = self.core.fill_gaps(
|
||||
&uid,
|
||||
&file,
|
||||
pending.meta.len,
|
||||
pending.meta.base_mtime,
|
||||
pending.meta.base_size,
|
||||
&authored,
|
||||
) {
|
||||
let _ = std::fs::remove_file(&path);
|
||||
error!(%uid, ?error, "cannot complete the queued revision a write open is based on");
|
||||
break 'install Err(error);
|
||||
}
|
||||
debug!(%uid, "write open completed an incomplete queued revision");
|
||||
}
|
||||
initial_written.add(0, pending.meta.len);
|
||||
}
|
||||
// Sampled *before* the state lock, never under it: no site in
|
||||
|
||||
Reference in new issue
Block a user