← Back to all posts
#03

From "/home/r/file.txt" to the Actual Bytes on Disk

May 2026

The Problem

You have a string: "/home/r/file.txt". You want the bytes of that file. Between your string and those bytes sit at minimum 4 disk reads and a chain of integer lookups.

Step-by-Step Walk (ext4, 4096-byte blocks, 256-byte metadata records)

STEP 1: Start at metadata record #2 (the root "/")

The filesystem reserves record #2 for the root. To find it on disk:

group = (2 - 1) / inodes_per_group = 1 / 8192 = 0  (group 0)
index = (2 - 1) % inodes_per_group = 1
byte_offset_in_table = 1 * 256 = 256

The metadata-record table for group 0 starts at some block B
(read from the group descriptor table).
Read block B, seek to offset 256 within it → that's the root's record.
        
STEP 2: Read root's data blocks → search for "home"

The root's metadata record has an extent array. The first extent says: file block 0 maps to physical block P. Read block P from disk. It contains a name-to-number table:

Offset  Record-#  Name-len  Name
0x00    2         1         "."
0x0C    2         2         ".."
0x18    131073    4         "home"     ← found it! record number = 131073
0x28    393217    3         "usr"
...
        

Each entry is: [4-byte record number] [2-byte entry length] [1-byte name length] [1-byte file type] [name bytes].

STEP 3: Load metadata record #131073 → search for "r"
group = (131073 - 1) / 8192 = 16  (group 16)
index = (131073 - 1) % 8192 = 0
byte_offset = 0 * 256 = 0

Read the metadata-record table block for group 16.
Read the data blocks of record #131073.
Search the name-to-number table for "r".
Found: record number = 131074
        
STEP 4: Load metadata record #131074 → search for "file.txt"
group = (131074 - 1) / 8192 = 16
index = (131074 - 1) % 8192 = 1
byte_offset = 1 * 256 = 256

Read that record. Read its data blocks.
Search name-to-number table for "file.txt".
Found: record number = 131080
        
STEP 5: Load metadata record #131080 → read the file's data
This record is NOT a directory, it's a regular file.
Fields we care about:
  i_size = 8500          (file is 8500 bytes)
  i_block[0..3] holds extent records:
    extent 0: logical block 0, physical block 525000, length 3

Read physical blocks 525000, 525001, 525002 from disk.
Concatenate. Trim to 8500 bytes. Done.
        

Disk Reads Counted

Read 1: root metadata record (#2)
Read 2: root's data block (name-to-number table of "/")
Read 3: record #131073 (the "home" directory metadata)
Read 4: "home"'s data block (name-to-number table)
Read 5: record #131074 ("r" directory metadata)
Read 6: "r"'s data block
Read 7: record #131080 ("file.txt" metadata)
Read 8: file.txt's data blocks (3 blocks)

Total: 8 disk reads minimum for /home/r/file.txt
    
DRAM caching saves most of these. The kernel keeps recently-read metadata records and name-to-number tables in DRAM. The root directory data is almost always cached. In practice, reading /home/r/file.txt might cost only 1-2 actual disk reads.

Why the In-Memory Name Cache Matters

The kernel maintains an in-memory table mapping (parent_record_number, name_string) → child_record_number. After the first walk, the pair (2, "home") → 131073 is cached. Next access skips the disk entirely for that component.

Cache hit rate for "/" → ~100%    (always cached)
Cache hit rate for "/home" → ~99% (almost always cached)
Cache hit rate for "/home/r" → ~95%
Cache hit rate for deep paths → varies by access pattern
    

ext4 Name-to-Number Table Entry Layout (On Disk)

Bytes [0..3]:   4-byte record number (little-endian)
Bytes [4..5]:   2-byte total entry length (for padding/alignment)
Byte  [6]:      1-byte name length
Byte  [7]:      1-byte file type (1=regular, 2=directory, 7=symlink)
Bytes [8..N]:   name characters (NOT null-terminated on disk)
    

Minimum entry size: 8 + 1 = 9 bytes, rounded to 4-byte boundary = 12 bytes. Maximum name: 255 bytes.

Grill: If s_inodes_per_group = 8192 and s_inode_size = 256, how many 4096-byte blocks does each group's metadata-record table occupy? Show the math. What is the byte offset of record #8193 within the table?