fix(fuse): keep the size of a file open for writing across re-listings

B109: an online create seals an empty file on Drive, and until the last
close the written bytes live only in the open handle. A re-listing of
the parent, a size upgrade or a create landing re-interned the node with
Drive's empty size, the kernel capped reads at that size, and SQLite
reported "database disk image is malformed". State::keep_open_write_size
now carries the handle's size and local mtime over in all three places.

The acceptance script waits for My files to mount after a daemon restart
instead of failing at once.
This commit is contained in:
nvrl committed 2026-09-29 16:49:13 +02:00
1 parent a2992b3b9d
commit f252b09757
6 files changed
+83 -9

No files matched your search

+23 -5
View File
@@ -12,6 +12,29 @@ Conventions:
---
## B109 — A re-listing shrinks a file that is open for writing, and SQLite reads it as malformed
**Status:** Fixed (unverified).
**Found:** 2026-09-29, by the acceptance case "application workloads" on My files. It failed
several runs in a row with `database disk image is malformed` at the `SELECT COUNT(*)` right
after a 500-row insert, and the daemon logged nothing. A diagnosis added to the case showed
`workload.db` at 0 bytes while SQLite still held it open with 16 KiB written.
**Where:** `crates/pdfs-fuse/src/state.rs` (`State::keep_open_write_size`, called from
`intern_mem_with_access`), the local-uid remap in `crates/pdfs-fuse/src/drain.rs`, and the size
upgrade in `crates/pdfs-fuse/src/lib.rs`.
**Cause.** An online create seals an empty file on Drive. Until the last close, the written
bytes live only in the open handle, and the entry's size is the handle's. Any re-listing of the
parent re-interned the node from Drive, which replaced that size with 0. Such re-listings come
from event echoes and sibling creates, and SQLite's journal is one. The kernel then got size 0
from `getattr` and ended SQLite's reads at EOF without asking the daemon, so the database read
back as empty pages. Queued writes were already protected (B11); open handles were not.
**Fix.** A node that has an open write handle keeps the handle's size and its local mtime when
a listing, a size upgrade or a create landing replaces its entry. After the last close, the
queued write takes over as before.
## B108 — A file deleted during a mirror pass puts the sync folder in `error`
**Status:** Fixed (unverified).
@@ -31,11 +54,6 @@ ended in `error` and logged "item(s) failed; will retry" for files nobody wanted
does not count as an error. The next pass sees the deletion and handles it like any other.
Other open failures are still errors.
**Also open from the same run.** "application workloads" on My files failed with
`database disk image is malformed` at the `SELECT` after the insert. The daemon logged nothing
for it. The case now reports the step that failed, the database and journal files beside it,
and which pages differ from a local replay of the same statements.
## B107 — One read on a stalled connection hangs the reading program for half an hour
**Status:** Fixed (unverified).
+3
View File
@@ -40,6 +40,9 @@ scratch (user data in `staging/` and `recovery/` is never touched by this).
- **Editors can save over a file several times in a row.** The fix for B104 could leave the
upload of a replaced file queued, and the next save onto the same name failed with
`Input/output error` (B106).
- **Databases and other files kept open while written no longer read back empty.** When the
folder was listed again while a file was still open, its size dropped to what Drive held,
often 0 bytes, and SQLite reported `database disk image is malformed` (B109).
- **Deleting files during a mirror pass no longer marks the folder as failed.** A file removed
after the pass planned its upload was counted as an error, so the folder showed `error` until
a later pass (B108).