mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(fuse): a restart no longer brings back the write of a file deleted while open (B188)
This commit is contained in:
1 parent
2baad6720d
commit
b47620651f
4 files changed
+80
-1
No files matched your search
@@ -1961,6 +1961,20 @@ impl Core {
|
||||
"staged bytes are superseded by a queued write; kept for recovery");
|
||||
continue;
|
||||
}
|
||||
if self
|
||||
.db
|
||||
.has_pending_op(&uid.to_string(), OP_TRASH)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// The node was deleted while a handle still read these bytes,
|
||||
// and the daemon stopped before its trash went out. Queued again,
|
||||
// they lost their blob when the trash landed and retried on it
|
||||
// for good (`docs/BUGS.md` B188).
|
||||
info!(%uid, blob = %blob.display(),
|
||||
"staged bytes belong to a node whose trash is queued; discarded");
|
||||
self.cache.discard_staged(&blob);
|
||||
continue;
|
||||
}
|
||||
let op = PendingOp {
|
||||
id: 0,
|
||||
kind: OP_REVISION.to_string(),
|
||||
|
||||
@@ -476,6 +476,49 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "mounts FUSE: run with `cargo test -p pdfs-fuse sim:: -- --ignored`"]
|
||||
fn a_restart_does_not_bring_back_the_write_of_a_file_deleted_while_open() {
|
||||
// The bytes kept for the open handle outlived a restart, which queued
|
||||
// them again as a revision; the trash then landed and removed them, and
|
||||
// the revision retried on a missing blob for good (B188).
|
||||
use std::io::Write;
|
||||
let drive = FakeDrive::new();
|
||||
let dir = scratch("unlink-open-restart");
|
||||
let daemon = Daemon::start(&dir, drive.client(1, Faults::lan())).unwrap();
|
||||
let path = daemon.mountpoint.join("f.txt");
|
||||
|
||||
std::fs::write(&path, b"0123456789").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || {
|
||||
daemon.pending().is_ok_and(|items| items.is_empty())
|
||||
&& drive.tree().get("f.txt") == Some(&Entry::File(Arc::new(b"0123456789".to_vec())))
|
||||
}));
|
||||
daemon.client.set_online(false);
|
||||
std::fs::write(&path, b"abcdef").unwrap();
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(&path)
|
||||
.unwrap();
|
||||
std::fs::remove_file(&path).unwrap();
|
||||
file.write_all(b"xy").unwrap();
|
||||
drop(file);
|
||||
|
||||
let daemon = daemon.restart().unwrap();
|
||||
daemon.client.set_online(true);
|
||||
assert!(
|
||||
wait_until(Duration::from_secs(30), || {
|
||||
daemon.pending().is_ok_and(|items| items.is_empty()) && drive.tree().is_empty()
|
||||
}),
|
||||
"{:?} {:?}",
|
||||
daemon.pending(),
|
||||
drive.tree()
|
||||
);
|
||||
|
||||
assert!(matches!(daemon.stop(), Ok(MountOutcome::Shutdown)));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "mounts FUSE: run with `cargo test -p pdfs-fuse sim:: -- --ignored`"]
|
||||
fn a_write_opened_as_an_upload_lands_takes_the_landed_revision_as_its_base() {
|
||||
|
||||
@@ -12,6 +12,26 @@ Conventions:
|
||||
|
||||
---
|
||||
|
||||
## B188 — A restart can leave a queued write retrying forever on a file deleted while open
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
**Found:** 2026-10-05, in the confirmation run on 2baad67 (`three_clients_one_writer_each`
|
||||
seed 12). With its link down, a client overwrote a file, deleted it while open, appended to it
|
||||
and restarted. After the restart a revision of the deleted file retried on "No such file or
|
||||
directory" until the run gave up.
|
||||
|
||||
**Where:** `Core::reconcile_staging` in `crates/pdfs-fuse/src/lib.rs`.
|
||||
|
||||
**Cause.** Since B186's fix, the queued trash of an open file keeps the staged bytes the handle
|
||||
reads, and the trash's landing removes them. A restart in between forgets which handle the bytes
|
||||
were kept for. The scan at mount found them owned by no op and queued them again as a revision of
|
||||
the node. The trash, queued first, landed and removed the bytes, and the revision kept retrying on
|
||||
a blob that was gone. B186's fix brought this in, so no release has it.
|
||||
|
||||
**Fix.** The scan at mount discards staged bytes whose node has a queued trash, as the trash would
|
||||
have. Test `a_restart_does_not_bring_back_the_write_of_a_file_deleted_while_open` in
|
||||
`crates/pdfs-fuse/src/sim/daemon.rs`.
|
||||
|
||||
## B187 — An uploaded file can leave a copy of itself in the cache until the next start
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
|
||||
@@ -9,7 +9,7 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
- **Phase 2**: local ids, inode = local id, one tree for every mount (2.10.0).
|
||||
- **Phase 3, first part**: mkdir, create, rename, unlink and rmdir on the mount are recorded
|
||||
locally and sent from the queue; `"local_first": false` brings back the old path (5e0f30c).
|
||||
- Fixes from the simulation and account runs since then: B125, B127, B130 and B141 to B187.
|
||||
- Fixes from the simulation and account runs since then: B125, B127, B130 and B141 to B188.
|
||||
- **Sync issues, first cut** (§6.2): refusals sorted into issues, shown at once in the app,
|
||||
tray and CLI; Export button and `pdfs sync export`; `pdfs sync issues`; Discard button and
|
||||
`pdfs sync discard`; a warning badge on the node in the file browser.
|
||||
@@ -113,6 +113,8 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
stopped before `three_clients`. The rerun on 3073052 found a create that waited out
|
||||
a retry after a write replaced its blob (B185, fixed), and a file deleted offline
|
||||
while open that lost the bytes its handle read (B186, fixed; older than 3.0.0).
|
||||
The run on 2baad67 found that a restart before such a file's trash went out queued
|
||||
its kept bytes again, for good (B188, fixed).
|
||||
- [ ] A clean account run. The one on a6f36d5 (2026-10-04, LAN: 427 passed, 12 failed)
|
||||
failed B113 and B121 on every on-demand mount, two mirror-to-mirror move cases, and
|
||||
the journal check. B121 was a bug (B149, fixed); B113's case still expected creates
|
||||
|
||||
Reference in new issue
Block a user