mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-04 00:47:00 +01:00
fix(fuse): skip unreadable folders in sync, never evict local-only files
An unreadable subfolder, such as a container's root-owned cache, failed every pass of its synced folder, so nothing else in the tree reached Drive (#33, B196). The scan now leaves such a path out, together with an entry that vanished mid-scan and a name that is not UTF-8, and the pass drops everything under it from classification, the wipe guard and the push pass's deletions. It is never read as deleted and never downloaded into. An unreadable or absent root still fails the pass. The activity feed names unreadable paths once each time the set changes. Two related bugs on the way to online-only: - The push pass read deletions from the raw baseline, so a synced path that later became ignored was trashed on Drive (B197). - The switch evicted everything once the folder was idle, including ignored paths such as .git/, symlinks, unreadable folders and files changed after the pass, none of which are on Drive (B198). It now refuses and names the path. Closes #33
This commit is contained in:
1 parent
c0d211238b
commit
7eb6aa0640
10 files changed
+778
-102
No files matched your search
@@ -310,8 +310,10 @@ its changes go through the queue like My files.
|
||||
A filesystem watcher (`notify`) and a remote poll every 120 s feed a debounced pass (2 s, at most
|
||||
30 s). Passes are serialized per daemon. A pass:
|
||||
|
||||
1. **Walks the local tree.** Any `readdir`, metadata or permission failure marks the scan
|
||||
incomplete, and an incomplete scan cannot delete anything.
|
||||
1. **Walks the local tree.** A path the user may not read, an entry that vanishes mid-scan and a
|
||||
name that is not UTF-8 are left out, and the pass drops everything under them, as it drops
|
||||
ignored paths, so a left-out path is never read as deleted. An unreadable root and any other
|
||||
I/O error fail the pass.
|
||||
2. **Walks the remote tree.**
|
||||
3. **Loads the baseline**, the `sync_entry` rows: both sides as of the last successful pass.
|
||||
4. **Classifies each path** by comparing local, remote and baseline, with `(mtime, size)` as the
|
||||
|
||||
@@ -100,6 +100,9 @@ a live account since.
|
||||
|
||||
| Entry | Title |
|
||||
|---|---|
|
||||
| [B198](#b198--switching-to-online-only-deletes-local-files-that-never-reached-drive) | Switching to online-only deletes local files that never reached Drive |
|
||||
| [B197](#b197--switching-to-online-only-trashes-synced-files-that-became-ignored) | Switching to online-only trashes synced files that became ignored |
|
||||
| [B196](#b196--one-unreadable-folder-stops-the-whole-synced-folder-from-syncing) | One unreadable folder stops the whole synced folder from syncing |
|
||||
| [B195](#b195--the-app-crashes-when-proton-asks-for-a-captcha-on-secureblue) | The app crashes when Proton asks for a CAPTCHA on secureblue |
|
||||
| [B193](#b193--deleting-a-file-whose-create-failed-can-trash-a-file-of-the-users-on-drive) | Deleting a file whose create failed can trash a file of the user's on Drive |
|
||||
| [B191](#b191--a-file-deleted-after-its-create-failed-and-was-written-again-can-stay-on-drive) | A file deleted after its create failed and was written again can stay on Drive |
|
||||
@@ -298,6 +301,95 @@ a live account since.
|
||||
|
||||
Newest first.
|
||||
|
||||
### B198 — Switching to online-only deletes local files that never reached Drive
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
**Found:** 2026-10-08, reading the switch while fixing B196.
|
||||
**Where:** `crates/pdfs-fuse/src/devices.rs`, `Core::apply_sync_folder_mode`;
|
||||
`crates/pdfs-fuse/src/sync.rs`, `mirror_tree_unsynced`
|
||||
|
||||
**Repro:** Mirror a project folder with a `.git/` in it, then switch it to **Online only**.
|
||||
`.git/` is gone from the disk and was never on Drive.
|
||||
|
||||
**Cause.** The switch evicts the whole local tree once the folder is `idle`. `idle` only says the
|
||||
last pass applied everything it tried, and a pass never tries an ignored path (`.git/` is ignored
|
||||
by default), a symlink, or a folder it cannot read (B196). It also defers a file that is open for
|
||||
writing without leaving `idle`, and a file can change after the pass ends. The eviction deleted
|
||||
all of those.
|
||||
|
||||
**Fix.** Before the mode is written and anything is evicted, the switch walks the folder against
|
||||
the baseline, as a move out of a mirror folder already does, and refuses when something is only on
|
||||
this disk. The message names the path; an ignored one is called excluded from sync. A tracked file
|
||||
deleted here but not yet on Drive also refuses. An ignored one does not, since its baseline row
|
||||
stays for good and would hold the switch forever.
|
||||
|
||||
**Test:** unit tests `a_fully_synced_mirror_folder_may_go_online_only`,
|
||||
`an_ignored_folder_only_this_disk_has_holds_the_switch_back`,
|
||||
`a_deleted_ignored_path_does_not_hold_the_switch_back`,
|
||||
`an_edit_after_the_last_pass_holds_the_switch_back`, `a_new_file_at_the_root_holds_the_switch_back`,
|
||||
`an_unreadable_folder_holds_the_switch_back` and `a_symlink_holds_the_switch_back`. The switch
|
||||
itself has not been driven against a live account.
|
||||
|
||||
---
|
||||
|
||||
### B197 — Switching to online-only trashes synced files that became ignored
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
**Found:** 2026-10-08, reading the push pass while fixing B196.
|
||||
**Where:** `crates/pdfs-fuse/src/sync.rs`, `Core::push_pass`; `crates/pdfs-fuse/src/sync/planner.rs`,
|
||||
`local_deletions`
|
||||
|
||||
**Repro:** Mirror a folder until `build/` is on Drive, add `build/` to `.pdfsignore`, then switch
|
||||
the folder to **Online only**. `build/` goes to the Drive trash, and the switch then deletes the
|
||||
local copy.
|
||||
|
||||
**Cause.** The push pass that runs before the switch read its deletions as "in the baseline, not in
|
||||
the local walk". The walk skips ignored paths, so every synced path that later became ignored read
|
||||
as deleted. A full pass does not do this: it drops ignored paths from classification, which is
|
||||
what the user guide promises ("Ignoring never deletes anything").
|
||||
|
||||
**Fix.** The push pass reads its deletions from the same filtered baseline the wipe guard checks:
|
||||
without ignored paths, and without paths the scan could not read (B196).
|
||||
|
||||
**Test:** unit tests `a_push_pass_never_reads_an_ignored_path_as_deleted` and
|
||||
`local_deletions_come_out_shallowest_first`.
|
||||
|
||||
---
|
||||
|
||||
### B196 — One unreadable folder stops the whole synced folder from syncing
|
||||
|
||||
**Status:** Fixed (unverified). The reporter has not run the fix yet.
|
||||
**Found:** 2026-10-08, [#33](https://github.com/narrrl/proton-drive-linux/issues/33). A synced
|
||||
`~/Documents` held a restic cache a Docker container had created, which the user may not read.
|
||||
Every pass failed with `Couldn't sync Documents: read …/restic/cache/…/snapshots: Permission
|
||||
denied`, and nothing else in `Documents` synced.
|
||||
**Where:** `crates/pdfs-fuse/src/sync.rs`, `walk_local_tree`; `crates/pdfs-fuse/src/sync/planner.rs`,
|
||||
`Unscanned`
|
||||
|
||||
**Repro:** In a mirror folder, `sudo mkdir locked && sudo chmod 700 locked`, then sync.
|
||||
|
||||
**Cause.** B55 made every scan failure fatal to the pass, because a path left out of the scan read
|
||||
as a local deletion and was trashed on Drive. That was safe, but a folder the user may not read is
|
||||
not transient: the pass failed on every poll, for good. The same held for a file deleted between
|
||||
the listing and its `stat`, which a busy folder hits now and then, and for a name that is not UTF-8.
|
||||
|
||||
**Fix.** The scan leaves such a path out and records it, and the pass drops everything under it
|
||||
from classification, the wipe guard and the push pass's deletions, as it already did for ignored
|
||||
paths. A left-out path is then neither deleted on Drive nor downloaded into. A folder that lists
|
||||
but does not allow `stat` is left out whole. An unreadable or absent root, and every other I/O
|
||||
error, still fails the pass. The activity feed names the unreadable paths once each time the set
|
||||
changes, and the folder stays `idle`. Adding the folder to `.pdfsignore` was already a workaround
|
||||
and still silences the report.
|
||||
|
||||
**Test:** unit tests `an_unreadable_subdirectory_is_left_out_and_the_rest_scans`,
|
||||
`a_listable_but_unsearchable_folder_is_left_out_whole`,
|
||||
`a_path_the_scan_left_out_is_never_a_local_deletion`,
|
||||
`unscanned_covers_a_path_and_what_is_under_it_but_not_its_siblings` and
|
||||
`a_non_utf8_name_is_left_out_and_reported`. `an_unreadable_root_fails_the_scan` and
|
||||
`an_absent_root_fails_the_scan` still hold.
|
||||
|
||||
---
|
||||
|
||||
### B195 — The app crashes when Proton asks for a CAPTCHA on secureblue
|
||||
|
||||
**Status:** Fixed (unverified). Nobody has run the fix on secureblue yet.
|
||||
@@ -4822,6 +4914,10 @@ subdirectory holding a file, asserting the error names the subtree),
|
||||
strictness must not cost a working pass, so a healthy tree still reports every entry and still
|
||||
skips symlinks by design.
|
||||
|
||||
B196 later relaxed this: a path the user may not read, an entry that vanishes mid-scan and a name
|
||||
that is not UTF-8 are left out of the pass instead of failing it, and the pass drops everything
|
||||
under them from classification, so the B55 guarantee holds without the wedge.
|
||||
|
||||
Not covered: transient I/O errors, and an entry that disappears between `read_dir` and `stat`.
|
||||
Both are races that would need a filesystem shim to force; the code path they take is the same
|
||||
`symlink_metadata` failure the permission test drives.
|
||||
|
||||
@@ -11,6 +11,20 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- **One unreadable folder stopped a synced folder from syncing at all.** A folder you may not
|
||||
read, such as a cache a container created, failed every pass, so nothing else in the synced
|
||||
folder reached Drive. Such a folder is now left out and named once in the activity feed, and
|
||||
everything around it syncs. A file deleted during a scan, and a name that is not UTF-8, no
|
||||
longer fail the pass either.
|
||||
([#33](https://github.com/narrrl/proton-drive-linux/issues/33))
|
||||
- **Switching a folder to online-only could delete files that were never on Drive.** Ignored
|
||||
folders such as `.git/`, symlinks, unreadable folders and files changed after the last sync were
|
||||
deleted with the local copy. The switch now refuses and names what is only on this computer.
|
||||
- **Switching to online-only trashed synced files that had become ignored.** Adding a synced path
|
||||
to `.pdfsignore` and then switching moved it to the Drive trash. Ignoring never deletes now, as
|
||||
the user guide says.
|
||||
|
||||
## [3.1.2] — 2026-10-08
|
||||
|
||||
Schema: **42**, unchanged.
|
||||
|
||||
+6
-1
@@ -96,7 +96,9 @@ app and in the web app.
|
||||
Each synced folder can also be switched to **Online only**. The local copy is replaced by a mount
|
||||
of the folder, so the files stay visible but take no disk space until opened. Switch with the
|
||||
**Synced / Online only** switch on the folder's card, or `pdfs sync mode <id> ondemand` and back
|
||||
with `pdfs sync mode <id> mirror`. `pdfs locations` lists every local place the service occupies.
|
||||
with `pdfs sync mode <id> mirror`. The switch waits until everything in the folder is on Proton
|
||||
Drive, and refuses while the folder holds something that never goes there: an ignored path such
|
||||
as `.git/`, a symlink, or a folder you may not read. The message names it. `pdfs locations` lists every local place the service occupies.
|
||||
|
||||
### Moving between synced folders and My files
|
||||
|
||||
@@ -146,6 +148,9 @@ places, and both apply:
|
||||
Rules are re-read on every pass. Ignoring never deletes anything: a file that becomes ignored is
|
||||
simply no longer tracked, and its copy on Proton Drive stays.
|
||||
|
||||
A folder you may not read, such as a cache a container created as root, is skipped the same way,
|
||||
and the activity feed names it once. Add it to `.pdfsignore` to stop that message.
|
||||
|
||||
## Changes waiting for Proton Drive
|
||||
|
||||
A change you made in the Proton Drive folder exists only on this computer until it reaches
|
||||
|
||||
Reference in new issue
Block a user