Last Minute Notes

File Systems and Storage

File systems and storage for engineers: files and directories, file descriptors and the open file table, inodes, hard vs soft links, allocation methods, free space, journaling and copy-on-write file systems, the VFS and common file systems (ext4, XFS, Btrfs, ZFS, APFS, NTFS), the page cache and fsync durability, HDD vs SSD, disk scheduling, and RAID.

📘 File Systems and Storage

A file system turns a device full of numbered blocks into named files and directories that survive crashes. This page covers how that works, why "write returned successfully" does not mean "the data is on disk", and how storage hardware shapes the design.

Table of Contents

  1. Files and Directories
  2. File Descriptors
  3. Inodes
  4. Hard Links and Soft Links
  5. On-Disk Layout and Allocation
  6. Crash Consistency: Journaling and Copy-on-Write
  7. The VFS and Common File Systems
  8. The Page Cache and Durability
  9. Storage Hardware
  10. Disk Scheduling
  11. RAID
  12. Questions

1. Files and Directories

A file is a named sequence of bytes plus metadata. The OS does not care about the format; that is the application's job.

Unix file typels -l prefix
Regular file-
Directoryd
Symbolic linkl
Character device (terminal, /dev/null)c
Block device (disk)b
Named pipe (FIFO)p
Unix domain sockets

A directory is a special file that maps names to inode numbers. Unix uses a single tree rooted at /, and other file systems are mounted onto directories in it.

PathContents
/etcConfiguration
/varVariable data: logs, spool, databases
/tmpTemporary files, often RAM-backed (tmpfs)
/proc, /sysVirtual file systems exposing kernel and process state
/devDevice files
/home, /rootUser directories
/usr, /bin, /libPrograms and libraries

Permissions

-rwxr-x---  1 alice devs  4096 Sep 24 10:00 deploy.sh
 |  |  |
 |  |  +-- others: no access
 |  +----- group: read, execute
 +-------- owner: read, write, execute

Octal: r = 4, w = 2, x = 1, so rwxr-x--- = 750. For directories, x means "may enter or traverse", and r means "may list names". Special bits: setuid (run as the file owner, like passwd), setgid, and sticky (in /tmp, only the owner can delete their files). umask sets default permissions for new files; ACLs and capabilities add finer control.


2. File Descriptors

A file descriptor (FD) is a small integer that a process uses to refer to an open file, socket, pipe, or device.

FDDefault
0stdin
1stdout
2stderr

The kernel keeps three levels of tables:

  • The open file entry holds the current offset and flags.
  • After fork, parent and child share open file entries, so they share the offset.
  • dup2(fd, 1) makes FD 1 point to the same entry as fd; this is how shells implement > redirection.
  • Each process has an FD limit (ulimit -n, often 1,024 soft by default); servers with many connections must raise it, or they fail with EMFILE: Too many open files.
  • lsof -p <pid> or ls -l /proc/<pid>/fd lists a process's open FDs.

3. Inodes

An inode stores everything about a file except its name.

Inode fieldExample
Type and permissionsRegular file, 0644
Owner and groupUID 1000, GID 1000
Size12,345 bytes
Timestampsatime (access), mtime (content modified), ctime (inode changed); some FSes add birth time
Link countNumber of directory entries pointing here
Block pointers or extentsWhere the data lives
stat notes.txt          # show inode number and metadata
ls -i                   # inode numbers
df -i                   # inode usage; a disk can be "full" with free space if inodes run out

Classic ext2/ext3 block pointers: 12 direct pointers, then single, double, and triple indirect blocks. With 4 KB blocks and 4-byte pointers, one indirect block holds 1,024 pointers, so single indirect covers 4 MB, double 4 GB, and triple 4 TB. Modern ext4 and XFS use extents instead: (start block, length) ranges, which describe large contiguous files compactly.

Deleting a file removes a directory entry and decrements the link count. The data is freed only when the link count reaches zero and no process has the file open. That is why deleting a huge log file that a process still writes to does not free space; lsof +L1 finds such deleted-but-open files.


Hard linkSoft (symbolic) link
What it isAnother directory entry for the same inodeA small file containing a path
Commandln target linkln -s target link
Same inode numberYesNo
Survives deleting the original nameYes, data staysNo, becomes dangling
Across file systemsNoYes
To directoriesNo (would allow cycles)Yes

Symlinks are used for version switching (current -> releases/2026-09-24) and atomic deploys (create a new symlink, then rename it over the old one).


5. On-Disk Layout and Allocation

A typical Unix file system layout:

| boot block | superblock | inode bitmap | data bitmap | inode table | data blocks ... |
PartHolds
SuperblockFS type, size, block size, counts, state; replicated for safety
BitmapsWhich inodes and blocks are free
Inode tableAll inodes
Data blocksFile contents and directory entries

ext4 groups these into block groups so a file's inode and data stay close together.

Allocation methodHowProsCons
ContiguousOne run of blocksFast sequential and random accessExternal fragmentation, files cannot grow
LinkedEach block points to the nextNo fragmentationSlow random access, one bad pointer loses the rest
FATThe chain of links in a separate tableSimple, cacheableTable size, still sequential walking (FAT32, exFAT on USB drives)
IndexedInode holds pointers to blocksGood random accessPointer overhead for small files
ExtentsInode holds (start, length) rangesCompact and fast for large filesFragmented files need many extents

6. Crash Consistency: Journaling and Copy-on-Write

Appending a block to a file updates several structures: the data block, the inode, and the data bitmap. A crash between those writes leaves the file system inconsistent (a block marked used but owned by nobody, or an inode pointing at garbage).

ApproachHowExample
fsckScan everything after a crash and repairext2; slow on large disks
JournalingWrite intended changes to a log first, then apply them; replay the log after a crashext4, XFS, NTFS
Copy-on-writeNever overwrite in place; write new blocks, then atomically switch a root pointerZFS, Btrfs, APFS
Log-structuredThe whole FS is an append-only logF2FS for flash; the same idea as LSM trees in databases

Journaling modes in ext4:

ModeJournalsTrade-off
journalMetadata and dataSafest, slowest (data written twice)
ordered (default)Metadata; data written before its metadata commitsGood balance
writebackMetadata onlyFastest, files may contain stale data after a crash

This is the same write-ahead logging idea databases use; see Database Internals.


7. The VFS and Common File Systems

The Virtual File System layer gives one API (open, read, write, stat) over many file system implementations, including virtual ones like /proc, network ones like NFS, and FUSE file systems in user space.

File systemWhereNotes
ext4Linux default for many distrosJournaling, extents, mature
XFSRHEL default, large serversExcellent parallel I/O and large files
BtrfsFedora, openSUSE default, SynologyCoW, snapshots, checksums, compression
ZFSFreeBSD, storage servers, ProxmoxCoW, end-to-end checksums, snapshots, built-in RAID (RAID-Z), send/receive replication
APFSmacOS, iOSCoW, snapshots, clones, encryption, SSD optimized
NTFSWindowsJournaling, ACLs; ReFS for resilient servers
FAT32 / exFATUSB drives, SD cardsPortable, no permissions or journaling; FAT32 caps files at 4 GB
tmpfs/tmp, /dev/shmLives in RAM (and swap)
overlayfsContainer imagesStacks read-only image layers with a writable top layer
NFS, SMBNetwork sharesRemote file access; locking and caching semantics differ from local

Checksums matter: hard drives and SSDs occasionally return silently corrupted data (bit rot), and only checksumming file systems like ZFS and Btrfs detect and, with redundancy, repair it.


8. The Page Cache and Durability

Reads and writes go through the page cache in RAM.

  • write() returning success means the data is in the kernel's memory, not on disk.
  • A power loss before writeback loses it.
  • fsync(fd) blocks until the file's data and metadata reach stable storage, including flushing the device's own write cache.
  • fdatasync skips non-essential metadata like timestamps.
  • O_DIRECT bypasses the page cache (databases with their own buffer pools use it).
  • O_SYNC makes every write synchronous.

Writing a file safely

// Atomic file replacement
int fd = open("config.json.tmp", O_WRONLY | O_CREAT | O_TRUNC, 0644);
write(fd, data, len);
fsync(fd);                                   // data is durable
close(fd);
rename("config.json.tmp", "config.json");   // atomic on POSIX file systems
int dir = open(".", O_RDONLY);
fsync(dir);                                  // make the rename itself durable
close(dir);

Readers see either the old or the new file, never a partial one. Skipping the directory fsync can lose the rename after a crash.

Durability is expensive: an fsync costs from tens of microseconds on enterprise NVMe with power-loss protection to many milliseconds on consumer drives and HDDs. Databases amortize it with group commit (one fsync for many transactions).

A famous lesson: in 2018 PostgreSQL discovered that after a failed fsync Linux may drop the dirty pages and report success on a retry, so the data is silently lost. PostgreSQL now panics and recovers from its WAL on fsync failure ("fsyncgate").


9. Storage Hardware

HDDSATA SSDNVMe SSD
HowSpinning platters, moving headFlash over a SATA interfaceFlash directly on PCIe
Random read latency5 to 10 ms (seek + rotation)About 100 microseconds10 to 100 microseconds
Random IOPS100 to 200Tens of thousandsHundreds of thousands to millions
Sequential throughput150 to 280 MB/sAbout 550 MB/s3 to 14 GB/s (PCIe 4 and 5)
Queues11 queue, 32 commandsUp to 64K queues, 64K commands each
Cost per TBLowestMediumMedium, falling
Best forArchives, bulk capacityLegacy systemsEverything performance sensitive

HDD access time = seek time + rotational latency + transfer time. At 7,200 RPM a rotation takes 8.3 ms, so average rotational latency is about 4.2 ms.

Flash quirks the OS and databases care about:

QuirkConsequence
Cannot overwrite in place; must erase large blocks firstThe flash translation layer (FTL) remaps writes, like a log-structured FS inside the drive
Limited program/erase cyclesWear leveling; endurance rated in drive writes per day (DWPD) or TBW
Write amplificationOne logical write may cause several physical ones; keep free space, use TRIM
TRIM / discardTells the drive which blocks are free so garbage collection is efficient
Garbage collection pausesTail latency spikes under sustained writes

The Linux block layer uses the multi-queue design (blk-mq) to match NVMe's parallel queues.


10. Disk Scheduling

For HDDs, the order of requests determines how far the head moves. Example: head at cylinder 53, queue 98, 183, 37, 122, 14, 124, 65, 67, disk cylinders 0 to 199.

AlgorithmOrder servedHead movement
FCFS98, 183, 37, 122, 14, 124, 65, 67640
SSTF (shortest seek first)65, 67, 37, 14, 98, 122, 124, 183236
SCAN (elevator, moving toward 0 first)37, 14, (to 0), 65, 67, 98, 122, 124, 183236 (53 to 0, then 0 to 183)
C-SCAN (moving toward 199, jump back)65, 67, 98, 122, 124, 183, (to 199, return to 0), 14, 37382 counting the return (183 without it)
LOOK / C-LOOKLike SCAN / C-SCAN but turn at the last request instead of the disk endLess than SCAN

SSTF can starve far requests; SCAN and C-SCAN give more uniform wait times.

Linux I/O schedulers today:

SchedulerUse
noneNVMe: the device handles parallelism and ordering
mq-deadlineSATA SSDs and HDDs: bounds latency, prevents starvation
bfqDesktops with slow disks: fairness and interactivity
kyberFast devices with latency targets

Check with cat /sys/block/nvme0n1/queue/scheduler.


11. RAID

RAID combines disks for performance, redundancy, or both.

LevelLayoutUsable capacity (n disks)SurvivesNotes
RAID 0StripingnNothingFast, any disk failure loses everything
RAID 1Mirroringn / 2 (for pairs)One disk per mirrorFast reads, simple
RAID 5Striping + distributed parityn - 11 diskSlow small writes (read-modify-write), long risky rebuilds on large disks
RAID 6Striping + double parityn - 22 disksSafer for large arrays
RAID 10Mirrored pairs, stripedn / 2One disk per pairBest performance plus redundancy, databases love it

Parity uses XOR: if P = A xor B xor C, then any lost block equals the XOR of the others.

RAID is not a backup. It protects against disk failure, not against deletion, corruption, ransomware, or losing the whole machine. Cloud block storage (EBS, Persistent Disk) is replicated behind the scenes; object storage (S3) uses erasure coding, a generalization of parity across many machines.


12. Questions

QuestionShort answer
What is an inode?Metadata and block locations of a file, everything except the name
Hard link vs soft link?Another name for the same inode vs a file containing a path
Why can a disk be full with free space?Out of inodes, or deleted files still held open
What is a file descriptor?Per-process integer handle to an open file entry
Does write() mean data is on disk?No, only in the page cache until writeback or fsync
How do you write a file atomically?Write temp, fsync, rename over the target, fsync the directory
What is journaling?Log intended metadata changes before applying them, replay after a crash
Journaling vs copy-on-write?Log then apply in place vs never overwrite, switch a root pointer
HDD vs SSD for random I/O?Milliseconds vs microseconds; seek and rotation vs no moving parts
What is write amplification?Flash writing more physically than logically because of erase blocks and GC
SSTF vs SCAN?Nearest request first (starvation) vs elevator sweep (fair)
RAID 5 vs RAID 10?Parity, capacity efficient, slow writes vs mirrored stripes, fast, half capacity
Is RAID a backup?No

Next: I/O and Linux Internals.

On this page