mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(fuse): a write closed just as its file's create lands is queued (B176)
This commit is contained in:
1 parent
ce60e006ac
commit
4f141c78ca
5 files changed
+56
-7
No files matched your search
@@ -1806,7 +1806,7 @@ impl ProtonFs {
|
||||
}
|
||||
(Some(mut h), false) => {
|
||||
self.core.follow_landed_create(&mut h);
|
||||
match self.core.queue_revision(&h) {
|
||||
match self.core.queue_revision(&mut h) {
|
||||
Ok(()) => reply.ok(),
|
||||
Err(e) => reply.error(e),
|
||||
}
|
||||
|
||||
@@ -3242,7 +3242,7 @@ impl Core {
|
||||
///
|
||||
/// The scratch file is *moved* into staging, never copied: it is the only
|
||||
/// copy of what the user wrote.
|
||||
fn queue_revision(&self, h: &WriteHandle) -> Result<(), Errno> {
|
||||
fn queue_revision(&self, h: &mut WriteHandle) -> Result<(), Errno> {
|
||||
// Closing an untouched writable handle is not a mutation. It remains
|
||||
// valid after a downgrade and only owes cleanup of its empty scratch.
|
||||
if !h.dirty {
|
||||
@@ -3266,9 +3266,15 @@ impl Core {
|
||||
// recovery a second, stale copy of the same write.
|
||||
self.cache.clear_scratch_durable(&h.path);
|
||||
if is_local_uid(&h.uid) && !self.db.has_create_op(&h.uid.to_string()).unwrap_or(true) {
|
||||
debug!(uid = %h.uid, "local node was unlinked before creation; dropping revision");
|
||||
let _ = std::fs::remove_file(&h.path);
|
||||
return Ok(());
|
||||
// A create that landed since the handle last looked left no op
|
||||
// either. It moved the row to its uid in the same transaction, so
|
||||
// the row says which it was (`docs/BUGS.md` B176).
|
||||
self.follow_landed_create(h);
|
||||
if is_local_uid(&h.uid) {
|
||||
debug!(uid = %h.uid, "local node was unlinked before creation; dropping revision");
|
||||
let _ = std::fs::remove_file(&h.path);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
// Materialize as much of the full content as is free to materialize. A
|
||||
// complete blob is uploadable without the network and lets a later write
|
||||
@@ -8780,6 +8786,26 @@ mod tests {
|
||||
assert_eq!(replaced.matches("self.withdraw_queued_ops(uid)").count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_write_closed_as_its_create_lands_is_queued() {
|
||||
// The release followed a landing create, found none, and the create
|
||||
// landed before it asked for the create op. Finding none, it took the
|
||||
// file for one unlinked before it was created and dropped the write
|
||||
// (B176). The landing removes the op and moves the row at once, so a
|
||||
// missing op is checked against the row.
|
||||
let revision = function_source(include_str!("lib.rs"), "fn queue_revision(");
|
||||
assert_before(
|
||||
revision,
|
||||
"self.db.has_create_op(",
|
||||
"self.follow_landed_create(h)",
|
||||
);
|
||||
assert_before(
|
||||
revision,
|
||||
"self.follow_landed_create(h)",
|
||||
"dropping revision",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_replaced_node_put_back_shows_in_the_mount_again() {
|
||||
// The restore after a failed rename left the node hidden and out of
|
||||
|
||||
@@ -12,6 +12,27 @@ Conventions:
|
||||
|
||||
---
|
||||
|
||||
## B176 — A write closed just as its file's create lands is dropped
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
**Found:** 2026-10-04, by `one_client_on_a_good_link` (seed 12) on 378667f, run three at a time
|
||||
in a loop. A write at offset 6 to a file whose create was landing never reached Drive; Drive
|
||||
ended with the file's first bytes and the later truncate only.
|
||||
|
||||
**Where:** `Core::queue_revision` in `crates/pdfs-fuse/src/lib.rs`.
|
||||
|
||||
**Cause.** A write handle closed on a file not yet on Drive first checks whether the create has
|
||||
landed and, if so, follows it to the uid Drive gave it. It then checks that the create is still
|
||||
queued; a file with neither was unlinked before it was created, and its write is dropped. A
|
||||
create that landed between the two checks looked like that, and the write was dropped.
|
||||
|
||||
**Fix.** The landing removes the create op and gives the row its uid in one transaction. So a
|
||||
missing create op is checked against the row, and a landed file's write is queued for its uid.
|
||||
|
||||
**Test:** `a_write_closed_as_its_create_lands_is_queued` in `crates/pdfs-fuse/src/lib.rs`.
|
||||
|
||||
---
|
||||
|
||||
## B175 — A file replaced by a rename just as its create lands stays on Drive
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
|
||||
@@ -78,6 +78,7 @@ by its local id, in the queue, the stored tree and pins alike.
|
||||
(B174).
|
||||
- **A file replaced by a rename just as it reached Drive is removed from Drive too.** It no
|
||||
longer stayed there, and the next rename of its name no longer moved it instead (B175).
|
||||
- **A write closed just as its file reached Drive is no longer lost** (B176).
|
||||
- **A file renamed just as its upload finished keeps its new name.** The mount no longer showed
|
||||
the old name, and a second rename no longer failed with "No such file or directory" (B152,
|
||||
B172).
|
||||
|
||||
@@ -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: B127 and B141 to B175.
|
||||
- Fixes from the simulation and account runs since then: B127 and B141 to B176.
|
||||
- **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`.
|
||||
|
||||
@@ -119,7 +119,8 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
on d850265 failed seed 12 once in nine runs: a file deleted just as its create
|
||||
landed stayed on Drive (B173, fixed). The suite on 378667f failed seed 6: a rename
|
||||
in the same gap was refused with `EACCES` (B174, fixed). The loop on 378667f failed
|
||||
seed 8: a file replaced by a rename in that gap stayed on Drive (B175, fixed).
|
||||
seed 8: a file replaced by a rename in that gap stayed on Drive (B175, fixed), and
|
||||
seed 12: a write closed as its file's create landed was dropped (B176, fixed).
|
||||
5. **Release**: 2.10.0 is tagged; the 2.9.0 tag still waits for the user's go-ahead.
|
||||
|
||||
## After 3.0.0
|
||||
|
||||
Reference in new issue
Block a user