[PR #956] [MERGED] fix(process): harden filesystem snapshot identity and cancellation #2534

Closed
opened 2026-10-17 21:07:15 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/956
Author: @morluto
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/7/2026
Merged by: @morluto

Base: main ← Head: fix/filesystem-snapshot-identity


📝 Commits (8)

  • 3d59430 fix(process): bind snapshots to captured file identity
  • 55e9e20 fix(process): honor snapshot cancellation for empty files
  • a008abd test(process): verify snapshot descriptor cleanup
  • 9d6a37f fix(process): preserve snapshot cancellation results
  • 530c41f fix(process): preserve non-cancellation capture failures
  • de647b8 test(process): verify descriptor cleanup with fstat
  • 6233006 fix(process): detect snapshot changes by ctime
  • 1b5be9f test(process): prove snapshot ctime invalidation

📊 Changes

8 files changed (+377 additions, -13 deletions)

View changed files

➕ src/application/FilesystemSnapshot.test.ts (+122 -0)
📝 src/application/FilesystemSnapshot.ts (+24 -6)
📝 src/application/ProcessCaptureLifecycle.ts (+2 -1)
📝 src/application/ProcessHarness.ts (+31 -5)
➕ tests/boundary/process/filesystemSnapshotFifoRace.test.ts (+54 -0)
📝 tests/boundary/process/processCaptureLifecycle.test.ts (+113 -1)
➕ tests/fixtures/filesystemSnapshotFifoProbe.mjs (+23 -0)
➕ tests/fixtures/processSnapshotCancellation.mjs (+8 -0)

📄 Description

Filesystem snapshots could block indefinitely if a regular file is replaced with a FIFO between lstat and open, and could pair a replacement file's digest with stale metadata. Cancellation from initial or final snapshot hashing also surfaced as a generic capture failure.

This change opens snapshot files with O_NONBLOCK, verifies the opened descriptor still matches the captured file identity and state before hashing, and rejects identity changes as incomplete snapshots. File-state comparisons include ctime, so a same-size rewrite with restored mtime cannot be paired with the prior snapshot metadata. It checks cancellation at asynchronous hashing checkpoints, including empty files, and closes descriptors on cancellation. The shared process harness maps actual abort failures to the typed cancelled result while retaining tagged errors and unrelated filesystem causes.

Validation:

  • npm run test:focused -- tests/boundary/process/processCaptureLifecycle.test.ts tests/boundary/process/filesystemSnapshotFifoRace.test.ts
  • npm run test:local -- src/application/FilesystemSnapshot.test.ts
  • npm run check:fast
  • npm run format:check
  • npm run check:ci
  • npm run verify:test-discovery

The FIFO regression runs in an owned bounded POSIX subprocess; that test is skipped on Windows. Process-capture cancellation and error-preservation boundary tests ran on the current macOS host. Descriptor cleanup is verified with a positive-control open handle and fstat. No Windows runtime validation was performed.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/956 **Author:** [@morluto](https://github.com/morluto) **Created:** 10/7/2026 **Status:** ✅ Merged **Merged:** 10/7/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/filesystem-snapshot-identity` --- ### 📝 Commits (8) - [`3d59430`](https://github.com/morluto/rea/commit/3d59430f02bedc24d7bd8183dd5ea7b749de0976) fix(process): bind snapshots to captured file identity - [`55e9e20`](https://github.com/morluto/rea/commit/55e9e2079e0364d7b8be71d8f367ebf696752762) fix(process): honor snapshot cancellation for empty files - [`a008abd`](https://github.com/morluto/rea/commit/a008abd514970175dabafa4ccafbad9112f3d637) test(process): verify snapshot descriptor cleanup - [`9d6a37f`](https://github.com/morluto/rea/commit/9d6a37fe9b2bf46745a723bd441b69d1aca3dfa0) fix(process): preserve snapshot cancellation results - [`530c41f`](https://github.com/morluto/rea/commit/530c41f79bfacf8f247a808ea526334a7e27da28) fix(process): preserve non-cancellation capture failures - [`de647b8`](https://github.com/morluto/rea/commit/de647b843a23885903ccb364a801cb7765a6c467) test(process): verify descriptor cleanup with fstat - [`6233006`](https://github.com/morluto/rea/commit/623300684638025fe0404e266edd92cdc1f8d504) fix(process): detect snapshot changes by ctime - [`1b5be9f`](https://github.com/morluto/rea/commit/1b5be9f5b0ec74ad2287def55e19374a5fbcf09b) test(process): prove snapshot ctime invalidation ### 📊 Changes **8 files changed** (+377 additions, -13 deletions) <details> <summary>View changed files</summary> ➕ `src/application/FilesystemSnapshot.test.ts` (+122 -0) 📝 `src/application/FilesystemSnapshot.ts` (+24 -6) 📝 `src/application/ProcessCaptureLifecycle.ts` (+2 -1) 📝 `src/application/ProcessHarness.ts` (+31 -5) ➕ `tests/boundary/process/filesystemSnapshotFifoRace.test.ts` (+54 -0) 📝 `tests/boundary/process/processCaptureLifecycle.test.ts` (+113 -1) ➕ `tests/fixtures/filesystemSnapshotFifoProbe.mjs` (+23 -0) ➕ `tests/fixtures/processSnapshotCancellation.mjs` (+8 -0) </details> ### 📄 Description Filesystem snapshots could block indefinitely if a regular file is replaced with a FIFO between `lstat` and `open`, and could pair a replacement file's digest with stale metadata. Cancellation from initial or final snapshot hashing also surfaced as a generic capture failure. This change opens snapshot files with `O_NONBLOCK`, verifies the opened descriptor still matches the captured file identity and state before hashing, and rejects identity changes as incomplete snapshots. File-state comparisons include `ctime`, so a same-size rewrite with restored `mtime` cannot be paired with the prior snapshot metadata. It checks cancellation at asynchronous hashing checkpoints, including empty files, and closes descriptors on cancellation. The shared process harness maps actual abort failures to the typed cancelled result while retaining tagged errors and unrelated filesystem causes. Validation: - `npm run test:focused -- tests/boundary/process/processCaptureLifecycle.test.ts tests/boundary/process/filesystemSnapshotFifoRace.test.ts` - `npm run test:local -- src/application/FilesystemSnapshot.test.ts` - `npm run check:fast` - `npm run format:check` - `npm run check:ci` - `npm run verify:test-discovery` The FIFO regression runs in an owned bounded POSIX subprocess; that test is skipped on Windows. Process-capture cancellation and error-preservation boundary tests ran on the current macOS host. Descriptor cleanup is verified with a positive-control open handle and `fstat`. No Windows runtime validation was performed. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:07:15 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2534
No description provided.