Inodes and Data Blocks

[!NOTE] This module explores the core principles of Inodes and Data Blocks, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.

1. The Physical Layout

When you save a file, the File System must make two critical decisions:

  1. Metadata: Where do I store the file’s attributes (size, owner, permissions)?
  2. Data: Where do I store the actual bytes?

The answer is the Inode (Index Node).

1. The Inode Structure

An Inode is a fixed-size data structure (usually 128 or 256 bytes) on the disk. It acts as the “control block” for a file.

What’s inside?

  • Mode: File type (regular, directory, link) and permissions (rwx).
  • Owner Info: User ID (UID) and Group ID (GID).
  • Size: Size of file in bytes.
  • Timestamps: Access (atime), Modify (mtime), Change (ctime).
  • Block Pointers: The map to the data.

[!IMPORTANT] The Inode does NOT contain the filename. The filename is stored in the directory data block, which maps the name “report.pdf” to “Inode #4521”.


2. Mapping Data: Pointers vs Extents

How does the Inode know which disk blocks belong to the file?

The Old Way: Block Pointers (Ext2/Ext3)

The Inode contains an array of pointers (usually 15):

  • Direct Pointers (12): Point directly to data blocks. Great for small files.
  • Indirect Pointer (1): Points to a block full of pointers.
  • Double Indirect (1): Points to a block of indirect pointers.
  • Triple Indirect (1): You get the idea. Allows massive files.

Problem: For a 1GB file, you need thousands of metadata blocks just to store pointers.

The Modern Way: Extents (Ext4, XFS, NTFS)

Instead of listing every single block, we store Extents.

  • Format: [Start Block, Count]
  • Example: “Blocks 1000 to 1050 belong to this file”.
  • Benefit: Much more compact for contiguous files. Reduces metadata overhead and fragmentation.

3. Interactive: The Inode Walker

Traverse the tree structure of a file system from Inode to Data Blocks.

Inode #15

Direct Pointers
Indirect Pointers

Disk Blocks

Click a pointer to follow the reference.
Waiting for input...

4. Hardware Reality: HDD vs SSD

Understanding Inodes explains why some operations are slow.

Seeking (HDD)

On a spinning hard disk, reading a file requires:

  1. Seek to Inode.
  2. Seek to Data Block 1.
  3. Seek to Data Block 2 (if fragmented). Each seek takes ~5-10ms. Reading 1000 tiny files (random IO) involves 1000+ seeks. This is why Random I/O is slow on HDDs.

IOPS (SSD)

SSDs don’t have moving heads. They have access latencies of ~10-100 microseconds. They excel at Random I/O compared to HDDs, but the file system overhead (CPU time to traverse inode pointers) becomes the new bottleneck.


5. Code Example: Inspecting Inodes

We can use system calls to peek at the raw Inode data.

package main

import (
	"fmt"
	"os"
	"syscall"
)

func main() {
	fileInfo, err := os.Stat("test_file.txt")
	if err != nil {
		// Create file if not exists
		os.Create("test_file.txt")
		fileInfo, _ = os.Stat("test_file.txt")
	}

	// Access the underlying data source (Syscall)
	stat := fileInfo.Sys().(*syscall.Stat_t)

	fmt.Printf("File: %s\n", fileInfo.Name())
	fmt.Printf("Inode Number: %d\n", stat.Ino)
	fmt.Printf("Device ID: %d\n", stat.Dev)
	fmt.Printf("Hard Links: %d\n", stat.Nlink)
	fmt.Printf("Owner UID: %d\n", stat.Uid)
	fmt.Printf("Block Size: %d\n", stat.Blksize)
	fmt.Printf("Blocks Allocated: %d\n", stat.Blocks) // 512-byte blocks
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;

public class InodeInspect {
    public static void main(String[] args) throws IOException {
        Path file = Paths.get("test_file.txt");
        if (!Files.exists(file)) Files.createFile(file);

        // Basic Attributes (Cross-platform)
        BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
        System.out.println("Size: " + attrs.size());
        System.out.println("Key (Simulated Inode): " + attrs.fileKey());

        // POSIX Attributes (Linux/Mac)
        // Provides access to real Inode info
        try {
            PosixFileAttributes posix = Files.readAttributes(file, PosixFileAttributes.class);
            System.out.println("Owner: " + posix.owner().getName());
            System.out.println("Group: " + posix.group().getName());
            // Java hides the raw Inode number in the public API,
            // but 'fileKey()' often maps to (dev, inode).
        } catch (UnsupportedOperationException e) {
            System.out.println("POSIX attributes not supported on this OS.");
        }
    }
}