ยง39.7โ€“39.10Durability, Atomic Rename, Metadata, and Removing Files

Part III OSTEP pp. 477โ€“480 ยท ~6 min read

  • fsync
  • metadata
  • inode
  • unlink

Reading and writing is the core, but a real interface needs more: durability guarantees, safe updates, a way to inspect files, and a way to remove them.

39.7 Writing Immediately With fsync()

When a program calls write(), the file system usually doesnโ€™t write to disk right away. For performance it buffers the data in memory (the page cache) and flushes it later โ€” seconds later. So write() returns fast, and only a crash in that window loses the data. Most of the time thatโ€™s fine; but a database building a correct recovery protocol needs to force data to disk on demand. Thatโ€™s fsync (int fd): it forces all dirty (not-yet-written) data for a file to persistent storage and returns only once those writes complete.

write() buffers in memory and returns fast; the data reaches disk only later โ€” unless you force it with fsync().
application
OS (page cache)
disk
write() returns before the data is safe
fsync(fd) forces it now
The subtle part: the directory too
step 1 / 8 ยท time flows downward

application: write(fd, buf, size)

int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
write(fd, buffer, size);
fsync(fd);   // returns only once the data is durably on disk

The subtle catch: for a newly created file you often must also fsync() the parent directory, so the directory entry that names the file is on disk too. Overlooking this causes real application-level bugs.

39.8 Renaming Files, Atomically

mv foo bar calls rename(char *old, char *new). Its key guarantee: rename() is (usually) atomic with respect to crashes โ€” if the machine dies mid-rename, the file ends up with either the old name or the new one, never a broken in-between. That makes it the backbone of the safe-update idiom an editor uses to change a file without risking corruption:

The safe-update idiom: write a temp file, fsync it, then rename it over the original. rename() is atomic across crashes, so a reader always sees either the whole old file or the whole new one.
foo.txtfoo.txt.tmpv1 (old)(none)goal: swap in v2 atomically

1Start: the original file

foo.txt holds version 1. We want to replace it with version 2 without ever exposing a half-written file.

step 1 / 3
int fd = open("foo.txt.tmp", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
write(fd, buffer, size);   // write the NEW version to a temp file
fsync(fd);                 // force it to disk
close(fd);
rename("foo.txt.tmp", "foo.txt");  // atomically swap it into place

This is exactly the crash-consistency concern we met with RAIDโ€™s consistent-update problem: an update that must appear all-or- nothing. Journaling file systems (ch42) generalize it.

39.9 Getting Information About Files

The file system keeps a fair amount of data about each file โ€” its metadata . stat()/fstat() (given a pathname or descriptor) fill in a stat structure:

The stat structure: metadata the file system keeps about every file (retrieved by stat()/fstat(), stored in the inode).
stat fieldwhat it holds
st_inost_inoinode number โ€” the file's low-level name
st_sizest_sizetotal size in bytes
st_modest_modeprotection / permission bits (covered in ยง39.16)
st_nlinkst_nlinknumber of hard links to this inode
st_uid / st_gidst_uid / st_gidowner user id / group id
st_blocksst_blocksnumber of disk blocks allocated to the file
st_atime / st_mtime / st_ctimest_atime / st_mtime / st_ctimelast access / last modification / last status-change time
Dotted-underlined cells have explanations โ€” click one.

All of this lives in a per-file on-disk structure called an inode โ€” think of it, for now, as the persistent record holding everything above. Inodes live on disk; copies of active ones are cached in memory for speed. (Weโ€™ll dissect the inode itself when we build the file system.)

39.10 Removing Files

So how do you delete a file? rm traces to a single, oddly-named system call:

prompt> strace rm foo
...
unlink("foo")   = 0
...

unlink() takes just the fileโ€™s name and returns 0 on success. But why unlink, not remove or delete? That name is a genuine clue about how the file system works underneath โ€” and to unravel it, we first need to understand hard links, which is exactly where the next page begins.

Check yourself: durability, rename, metadata, removal

1.After write() returns successfully, is the data safely on disk?

2.What does fsync(fd) do?

3.To durably create a NEW file, why might you need to fsync() the parent directory too?

4.Why is the write-temp โ†’ fsync โ†’ rename idiom crash-safe?

5.What is file metadata, and where is it stored?

6.Which system call does rm use to remove a file, and what is the immediate puzzle?

6 questions