Home/Learn/Operating Systems/File System Structure & Inodes

File System Structure & Inodes

Intermediate
File Systems

An inode is a metadata structure that stores file attributes and pointers to data blocks; the directory system maps human-readable names to inode numbers.

Overview

Every file in a Unix-like filesystem has an inode — a fixed-size metadata record stored in the inode table. The inode holds file size, permissions (rwxrwxrwx), owner UID/GID, timestamps (atime/mtime/ctime), link count, and an array of pointers to data blocks (direct, single-indirect, double-indirect, triple-indirect). The file's name is NOT in the inode; names live in directory entries (dentries), which are simply mappings of name → inode number. This design enables hard links (multiple names pointing to the same inode) and makes file renaming within a filesystem an O(1) operation. The superblock stores filesystem-level metadata.

Inode Structure and Block Pointers

A typical inode has 12 direct block pointers, 1 single-indirect pointer (points to a block of pointers), 1 double-indirect pointer, and 1 triple-indirect pointer. For 4 KB blocks and 4-byte pointers, a single-indirect block holds 1024 pointers. This hierarchical structure allows small files to be accessed quickly (direct blocks) while supporting very large files.

Java — Inode structure and max file size calculation
// Inode structure (pseudocode representation)
class Inode {
    int    fileSize;           // bytes
    short  permissions;        // rwxrwxrwx bitmask
    int    ownerId;
    int    groupId;
    long   accessTime;         // atime: last read
    long   modifyTime;         // mtime: last content write
    long   changeTime;         // ctime: last metadata change
    int    linkCount;          // number of hard links pointing here
    int    blockCount;         // number of 512-byte blocks allocated

    // Block pointers (ext2/ext3 style)
    int[]  directBlocks    = new int[12];  // 12 × 4KB = 48 KB directly
    int    singleIndirect;  // → block of 1024 pointers → 4 MB
    int    doubleIndirect;  // → block → 1024 blocks of pointers → 4 GB
    int    tripleIndirect;  // → 4 TB (rarely reached)
}

// Max file size calculation (4KB blocks, 4-byte pointers):
long blockSize    = 4096;           // 4 KB
long ptrsPerBlock = blockSize / 4;  // 1024 pointers per indirect block

long direct       = 12 * blockSize;                          //      48 KB
long singleInd    = ptrsPerBlock * blockSize;                //       4 MB
long doubleInd    = ptrsPerBlock * ptrsPerBlock * blockSize; //       4 GB
long tripleInd    = ptrsPerBlock * ptrsPerBlock * ptrsPerBlock * blockSize; // 4 TB

System.out.printf("Max file size ≈ %d TB%n",
    (direct + singleInd + doubleInd + tripleInd) / (1024L*1024*1024*1024)); // ~4 TB

Hard Links vs Symbolic Links in Java

A hard link creates a new directory entry pointing to the same inode — both names are equally valid, and the inode's link count is incremented. Deleting one name decrements the count; the data is freed only when count reaches zero. A symbolic link is a separate inode containing the path to the target — it can cross filesystems but breaks if the target is deleted.

Java — Hard link vs symbolic link (Files API)
import java.nio.file.*;
import java.nio.file.attribute.*;

Path original = Path.of("/tmp/data.txt");
Files.writeString(original, "Hello, filesystem!");

// HARD LINK: same inode, different directory entry
Path hardLink = Path.of("/tmp/data-hard.txt");
Files.createLink(hardLink, original); // both point to same inode

// Verify: same inode number
BasicFileAttributes origAttrs = Files.readAttributes(original,
    BasicFileAttributes.class);
BasicFileAttributes hardAttrs = Files.readAttributes(hardLink,
    BasicFileAttributes.class);
System.out.println("Same file key (inode): " +
    origAttrs.fileKey().equals(hardAttrs.fileKey())); // true

// SYMBOLIC LINK: different inode, stores path to target
Path symLink = Path.of("/tmp/data-sym.txt");
Files.createSymbolicLink(symLink, original);

System.out.println("Symlink target: " + Files.readSymbolicLink(symLink));
System.out.println("Is symlink: " + Files.isSymbolicLink(symLink)); // true
System.out.println("Is symlink: " + Files.isSymbolicLink(hardLink)); // false

// Delete original — symlink breaks, hard link still works
Files.delete(original);
System.out.println("Hard link readable: " + Files.exists(hardLink)); // true
System.out.println("Symlink valid:      " + Files.exists(symLink));  // false

Key Points to Remember

  • 1An inode stores file metadata and block pointers but NOT the filename — filenames live in directory entries.
  • 2Directories are files too: they contain a list of (name, inode_number) pairs.
  • 3Hard links share an inode; the file is deleted only when link count reaches zero.
  • 4Symbolic links are separate inodes containing a path string; they can cross filesystems and can dangle.
  • 5The superblock contains filesystem-level metadata: total inodes, total blocks, block size, free inode/block counts.
  • 6stat <filename> in the terminal shows the inode number and all inode fields; ls -i shows inode numbers.

Interview Questions

Sign in to ask Aria
1

What is an inode and what information does it store?

EasyAmazon
2

What is the difference between a hard link and a symbolic link?

EasyGoogle
3

How does the kernel resolve a file path like /home/user/docs/file.txt to an inode?

MediumMicrosoft
4

Why can you rename a file within the same filesystem in O(1) but moving across filesystems requires a copy?

HardUber

Ask Aria about File System Structure & Inodes

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…