Master the fundamental concepts of file systems through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksJournaling filesystems log metadata changes before applying them to the main structures. After a crash, replay brings the on-disk state to a consistent point. ext3/ext4 and xfs use variations of this pattern.
Typical metadata update:
For example, creating a file might journal "allocate inode 42, insert dentry foo" before the inode table reflects inode 42 as in use.
Write-ahead logging is exactly how ext3, ext4, and NTFS survive power failures without running a multi-hour fsck, replaying only committed transactions and rolling back the rest. Get the commit-flag check backwards and you either lose a completed write or, worse, replay a half-written transaction and corrupt the filesystem it was supposed to protect.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will implement a simple WAL with redo records for create and unlink operations. The task asks you to simulate crash mid-transaction and prove replay restores consistency.
Implement write-ahead logging as ext3/ext4 journaling uses it. Block updates are grouped into transactions and written to a journal first, followed by a commit record. They reach their home location on disk only at a checkpoint. After a crash, recovery replays every committed transaction from the journal and discards any transaction whose commit record never made it. Transactions are therefore all-or-nothing, even if power fails halfway through.
0..15, all initially 0 (survives crashes).| Command | Effect | Output |
|---|---|---|
begin | open transaction Tn (numbered from 1; numbers are never reused) | begin T1 |
write B V | record block B = V in the open transaction (a later write to the same block replaces the earlier one) | write T1 block 3 = 42 |
commit | append the open transaction to the journal with its commit record | commit T1: 2 blocks journaled |
read B | the block's current value: the open transaction's write if any, else the newest committed journal write, else disk | read block 3 = 42 (from journal): source is transaction, journal or disk |
checkpoint | copy every committed transaction to disk in journal order, then empty the journal | checkpoint: T1 T2 -> 3 blocks written (checkpoint: nothing committed if there is no committed transaction) |
crash | lose the open transaction | crash: lost open T3 or crash: nothing in flight |
tornc | crash during commit: the open transaction's writes reach the journal but its commit record does not | tornc: T3 journaled without commit record |
recover | replay committed transactions to disk in journal order, discard the rest, empty the journal | one line per journal transaction, recover: replay T1 (2 blocks) or recover: discard T3 (no commit record), then recover: done |
show | print disk: B=V ... (non-zero blocks in order, or disk: all zero) and journal: T1 T2* (* marks uncommitted, or journal: empty) |
In checkpoint, the block count counts each write, so a block written by two transactions counts twice. checkpoint leaves uncommitted journal entries (from tornc) in place.
Errors: begin with a transaction open → error: T1 already open. write/commit/tornc without one → error: no open transaction. A block outside 0..15 → error: bad block B.
Input:
cLoading…
Output:
cLoading…
crash can only clear the volatile part.Hidden tests cover a torn commit that recovery discards while replaying the committed transactions before it, a checkpoint after several commits (including a block written twice), reads from each source, and every error.