diff --git a/Cargo.lock b/Cargo.lock index 6965f78..218a0e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3538,7 +3538,7 @@ checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" [[package]] name = "pdfs-cli" -version = "1.7.1" +version = "1.8.0" dependencies = [ "anyhow", "clap 4.6.4", @@ -3553,7 +3553,7 @@ dependencies = [ [[package]] name = "pdfs-core" -version = "1.7.1" +version = "1.8.0" dependencies = [ "async-trait", "directories", @@ -3574,7 +3574,7 @@ dependencies = [ [[package]] name = "pdfs-fuse" -version = "1.7.1" +version = "1.8.0" dependencies = [ "fuser", "futures", @@ -3591,7 +3591,7 @@ dependencies = [ [[package]] name = "pdfs-gui" -version = "1.7.1" +version = "1.8.0" dependencies = [ "anyhow", "async-channel", diff --git a/Cargo.toml b/Cargo.toml index a440730..38ce645 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["crates/pdfs-core", "crates/pdfs-fuse", "crates/pdfs-cli", "crates/pdfs-gui"] [workspace.package] -version = "1.7.1" +version = "1.8.0" edition = "2024" license = "MIT" authors = ["Nils Pukropp "] diff --git a/docs/BUGS.md b/docs/BUGS.md index ddb2126..e04186e 100644 --- a/docs/BUGS.md +++ b/docs/BUGS.md @@ -12,6 +12,60 @@ Conventions: --- +## B83 — An access-deferred op retries forever and is reported as an ordinary queued upload + +**Status:** Partly fixed (unverified) 2026-08-16 — the reporting half is in and +unit-tested; the cause (an unknown node reading as `EACCES`) is still open, see +"Still open" below. +**Found:** 2026-08-16, running `scripts/fuse-acceptance.sh --live` on a +throwaway CLI-made mount. Four regression cases (B69, B70, B74, B79) failed with +`TimeoutError: daemon mutation queue did not drain` — the suite's +`wait_for_queue()` polls the *global* `status.mount.pending_uploads`, which had +been sitting at 28 for 30 days on the development machine. `pdfs status` called +them 28 queued uploads holding 18.4 GiB; `pdfs transfers` showed nothing; +`failing_ops` and `parked_uploads` were both 0. +**Where:** `run_pending_drain` / `run_authorized_drain` +(`crates/pdfs-fuse/src/drain.rs`), `require_uid_access` +(`crates/pdfs-fuse/src/lib.rs`), `Db::effective_node_access` +(`crates/pdfs-core/src/db/share_access.rs`), +`Db::defer_op_without_attempt` (`crates/pdfs-core/src/db/ops.rs`). + +**Root cause.** `effective_node_access` returns `Ok(None)` when the uid has no +row in `nodes`, and `require_uid_access` maps that to `EACCES` — "I have never +heard of this node" and "you may not write this node" are the same answer. The +drain reads `EACCES` as `DrainDisposition::AccessDeferred` and calls +`defer_op_without_attempt`, which by design touches neither `attempts` nor +`last_error`. So the op was re-deferred every `DRAIN_ACCESS_RECHECK` (5s) +indefinitely, and every channel that could have surfaced it stayed empty: +`attempts` 0, `last_error` NULL, `parked` counts only `PARK_UNTIL` rows, +`failing` counts only `attempts >= FAILING_ATTEMPTS`, and the one log line was +`debug!`. On the machine that found it, all 28 uids were absent from `nodes`: + +```sql +select (select count(*) from pending_op p + where exists(select 1 from nodes n where n.uid = p.uid)) as in_nodes, + (select count(*) from pending_op) as total; +-- 0 | 28 +``` + +**Fix (this half).** Schema v27 adds `pending_op.access_deferred_since`. +`Db::defer_op_for_access` stamps it on the first deferral of a run and returns +the stamp on every one; `Core::defer_for_access` warns on the first deferral and, +once the run passes `DRAIN_ACCESS_DEFER_LIMIT` (5 min), reports it through +`record_op_failure` so it enters the ordinary backoff and shows up in +`failing_ops` and the status error text. `record_op_failure` clears the window so +each escalation re-arms it rather than firing every recheck, and an attempt the +access check admits clears it too (`clear_op_access_deferral`). The row and its +staged blob are untouched on every path — this changes only what the user can +see. + +**Still open:** the conflation itself. A revision op whose node has left the +local tree should refetch the node and either proceed or fail on a real remote +answer (404/trashed), not be deferred on a local guess. Same trap applies to +`create`/`mkdir`, whose authority is the parent uid. Also unaddressed: the +acceptance runner waits on the *global* queue, so any unrelated stuck op fails +those four cases on a machine that has one. + ## B82 — A `parent_uid` cycle hangs the daemon on any short search **Status:** Fixed (unverified) 2026-08-12 — unit-tested, not driven against a diff --git a/packaging/PKGBUILD b/packaging/PKGBUILD index 64e40fd..94df826 100644 --- a/packaging/PKGBUILD +++ b/packaging/PKGBUILD @@ -9,7 +9,7 @@ # Build + install: cd packaging && makepkg -fi pkgname=proton-drive-linux -pkgver=1.7.1 +pkgver=1.8.0 pkgrel=1 pkgdesc="Proton Drive client for Linux: FUSE files-on-demand mount, CLI, GTK4 app + tray" arch=('x86_64')