Skip to content

Handle.write() does not update inode metadata atomically #286

@shc0743

Description

@shc0743

@zenfs/core version

2.5.0

Host

Chrome 143

Minimal reproduction

A reproduction is on https://stackblitz.com/edit/stackblitz-starters-xjlpfzck?file=main.js
To reproduce, please follow the instruction on the page.

Logs

No response

Expected behavior

When a write operation is interrupted (e.g., tab closed), the file should remain consistent:

  • Either fully roll back to the old content
  • Or fully update to the new content
  • Never end up in an inconsistent state where data and metadata don't match

Actual behavior

When interrupted during write:

  1. Data block is partially written and persisted
  2. inode.size remains at old value (not updated in same transaction)
  3. Atomicity is broken - data and metadata updates are split across transactions
  4. Result: Corrupted file (truncated data, JSON parse fails)

Screenshots

Image

Additional context

Description

When writing to an existing file using IndexedDB backend, StoreFS.write() updates the file data but does not update inode metadata (size field) in the same transaction. This breaks atomicity - if the write is interrupted (tab close/reload), data and metadata become inconsistent.

Detailed Steps to Reproduce

  1. Set up an IndexedDB-backed file system using @zenfs/dom.
  2. Write a file (e.g., test.json) with some content.
  3. Overwrite or append to the same file with longer content.
  4. If the operation above was interrupted, such as tab reload or close, read again and the returned data is truncated or fails to parse.
  5. Inspect the IndexedDB store directly (via browser DevTools). The raw data is intact, but the inode's size field remains at the old value.

Root Cause

In https://github.com/zen-fs/core/blob/75f4aee73167bb0a03dbf92d22520302fb9af896/src/backends/store/fs.ts ,

  • Data update (tx.set(inode.data, buffer)) happens in one transaction
  • inode update (size/mtime) happens in a separate transaction or not at all
  • These two operations should be atomic (all or nothing) but are split

Suggested Fix

Ensure both operations are in the same transaction


Thank you for maintaining this project! Let me know if you need any further information or if I can help test a fix.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions