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:
- Metadata: Where do I store the file’s attributes (size, owner, permissions)?
- 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
Disk Blocks
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:
- Seek to Inode.
- Seek to Data Block 1.
- 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.");
}
}
}