linux-sysprog · intermediate · ~15 min
Memorize the canonical atomic-write recipe.
Return the canonical atomic-write step sequence. Use the constants:
enum {
STEP_WRITE_TMP = 1, // 1. write the new content to a temp file alongside the target
STEP_FSYNC = 2, // 2. fsync the temp file's contents to disk
STEP_FSYNC_DIR = 3, // 3. fsync the parent directory so the rename will persist
STEP_RENAME = 4, // 4. atomically rename the temp file over the target
};
Implement int atomic_write_steps(int *out, int max) that fills out[] with the steps in order and returns the count (always 4).
The order matters: writing+fsync FIRST guarantees the new contents are durable before the rename takes effect. Fsync of the directory captures the rename itself.
int out[8];
atomic_write_steps(out, 8)
-> 4, out == [STEP_WRITE_TMP, STEP_FSYNC, STEP_FSYNC_DIR, STEP_RENAME]
atomic_write_steps(out, 2)
-> 2, out == [STEP_WRITE_TMP, STEP_FSYNC] // truncated
Skipping fsync of the directory causes the rename to vanish on crash — your file may exist with old content or not at all. The Linux kernel devs and the SQLite docs both spell this out; learners almost never see it until they ship a bug.
Naïve fopen("w") + write can leave a half-written file on crash. The atomic-write pattern (write to temp, fsync, rename) is what every database, every text editor, and every config-rewriting tool actually does — and very few learners are taught it explicitly.
out[] array + max length.
Count of steps written.
No syscalls actually executed — this is a recipe-encoding exercise.
enum {
STEP_WRITE_TMP = 1, STEP_FSYNC = 2, STEP_FSYNC_DIR = 3, STEP_RENAME = 4
};
int atomic_write_steps(int *out, int max) { /* TODO */ return 0; }
Forgetting fsync of the directory (step 3). Writing the temp without fsync first (step 2). Renaming first then fsync (race window — leaves the file with old content visible).
max < 4 — truncate. max == 0 — return 0.
O(1).
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.