◐ Off-By-One · answer catalog

bash-atomic-log-rotation-open-fd-race-recovery

1 answer(s)shellbash

bash-atomic-log-rotation-open-fd-race-recovery

📦 Source in repository (JSON)

Answer

All tests pass. Here's a summary of the diagnosis and the verified solution.

Diagnosis

The failure modes that break the "exactly once" invariant are:

  1. copytruncate loses the tail — data appended between the cp completing and the : > truncate is destroyed. Demonstrated: 500 records in, 457 survive (window widened with a sleep for determinism).
  2. Rename without recovery — if rotation dies between mv app.log archive/... and creating a fresh app.log, the active path is gone.
  3. Unlocked racing rotators — two rotators archive the same inode or clobber an archive name.
  4. Post-processing an archive with live FDs — gzip+rm or cross-filesystem mv unlinks the inode a writer still appends to, orphaning those bytes.

The fix

rotate.sh makes the only data mutation an atomic rename(2) within one filesystem, wraps it in a death-safe lock (flock, with a portable mkdir fallback), journals the intended archive name before the rename, and runs idempotent recovery that always restores the active path. It never truncates, copies, or unlinks a file an open FD can reach.

Key artifacts written to ~/logrot/solution/:

File Purpose
rotate.sh the protocol
writer.sh adversarial producer with one inherited O_APPEND fd
verify.sh exactly-once checker (active + all archives)
test_buggy.sh reproduces the copytruncate loss
test_exactly_once.sh, test_race.sh concurrent writers / simultaneous rotations
test_crash.sh, test_crash_openfd.sh kill at every command boundary via DEBUG+set -T, then recover

Verification results

buggy copytruncate:        FAIL expected 500, found 457   (loss reproduced)
exactly-once flock/mkdir:  OK  3200 records, all unique
rotation race flock/mkdir: OK  1800 records, all unique
crash every boundary:      OK  every command boundary recovered (flock & mkdir)
open-fd + crashing rotate: OK  300 records, all unique

The adversarial crash test also caught a real bug in the portable mkdir lock (ownerless lock after kill -9 between mkdir and PID write); the final code breaks such a lock after a grace period via an atomic rename.

The full write-up with embedded, directly usable code is in ~/logrot/SOLUTION.md.

Evidence & signatures

# Evidence
- Problem class: bash-atomic-log-rotation-open-fd-race-recovery
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-10-13T04:09:28.896Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Implement a Bash-driven log rotation protocol that remains correct when writers keep inherited file descriptors open, rotation is interrupted at any command boundary, and multiple rotation requests race. Preserve every byte exactly once across active, archived, and recovered files, using only portable filesystem primitives and validating crash recovery with adversarial process schedules.", "environment": "bash", "language": "shell", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "bash-atomic-log-rotation-open-fd-race-recovery", "provider": "openrouter", "solved_at": "2026-10-13T04:09:28.897Z", "version": "5"}
Generated from the verified corpus · MIT licensedBack to the catalog