courses

ext File System Forensics

The ext file system family (ext2, ext3, ext4) is the default on most Linux distributions and is one of the most commonly encountered file systems in forensic investigations involving Linux servers, workstations, Android devices, and embedded systems. Understanding its on-disk structure is necessary to accurately interpret evidence, recover deleted files, and detect tampering.

Overview and History

Version Year Key Feature
ext2 1993 No journaling; foundation for the family
ext3 2001 Journaling added to ext2
ext4 2008 Extents, large file/volume support, delayed allocation, optional journaling enhancements

ext4 is backward-compatible with ext3 and ext2 and is the file system most investigators will encounter on modern Linux systems. The forensic principles are largely the same across all three versions; differences are noted where relevant.

On-Disk Layout

An ext file system is divided into fixed-size block groups. Each block group contains a complete, self-contained set of file system structures. This design provides redundancy and reduces head seek times.

[ Boot Block ][ Block Group 0 ][ Block Group 1 ][ ... ][ Block Group N ]

Each block group contains:

Structure Purpose
Superblock (copy) File system parameters; copy in group 0 is primary
Group Descriptor Table (copy) Location of structures within each group
Block Bitmap Allocation status of every block in this group
Inode Bitmap Allocation status of every inode in this group
Inode Table Fixed array of inode structures for this group
Data Blocks File content and directory data

The block size is set at format time (typically 1 KB, 2 KB, or 4 KB) and cannot be changed without reformatting. All addresses in the file system are in units of blocks.

Superblock

The superblock is the most critical structure in the file system. It is always located 1024 bytes from the start of the partition (regardless of block size) in block group 0, and is copied to every (or every other) block group as a backup.

Key fields:

Field Forensic Value
s_inodes_count Total inodes; limits maximum number of files
s_blocks_count Total blocks; combined with block size gives volume size
s_free_inodes_count / s_free_blocks_count Inconsistency with actual bitmaps may indicate tampering
s_mtime Time the file system was last mounted
s_wtime Time the superblock was last written
s_mkfs_time (ext4) Time the file system was created
s_last_mounted Path at which the file system was last mounted
s_uuid Universally unique identifier for the file system
s_volume_name Optional label
s_creator_os OS that created the file system
s_feature_* Feature flags indicating ext3/ext4 capabilities in use

Forensic note: s_mkfs_time records when the file system was created. If a suspect formatted a drive after being notified of an investigation, this timestamp will post-date the notification. Discrepancies between s_mtime (last mount) and s_mkfs_time can also indicate whether the file system has ever been used. Backup superblock copies in other block groups can be compared to the primary superblock to detect tampering with the primary copy.

Inodes

Every file, directory, symlink, device node, and pipe is represented by an inode (index node). An inode stores all metadata about a file except its name.

Key inode fields:

Field Contents
i_mode File type and permission bits
i_uid / i_gid Owner user and group IDs
i_size File size in bytes
i_atime Last access time
i_ctime Inode change time (not creation time — see below)
i_mtime Last data modification time
i_crtime (ext4 only) File creation time
i_dtime Deletion time (set when the inode is freed)
i_links_count Number of hard links to this inode
i_blocks Number of 512-byte sectors allocated
Block pointers Locations of data blocks (direct, indirect, double-indirect, triple-indirect in ext2/3; extents in ext4)

Forensic note — i_ctime is not creation time: A common mistake is treating i_ctime as the file creation time. It is the inode change time — the last time the inode’s metadata was modified (e.g., by a chmod, chown, or link count change). The actual creation time is only available in ext4 via i_crtime. On ext2/3, there is no reliable creation timestamp; i_mtime is often used as a proxy.

Forensic note — i_dtime: When a file is deleted, its inode’s i_dtime field is set to the deletion time and i_links_count is decremented to 0. The inode is then marked free in the inode bitmap, but the inode structure itself may remain on disk until it is reused. If the inode has not been reused, i_dtime gives a precise timestamp for when the file was deleted.

Inode Numbering

Inodes are numbered starting at 1. Several low-numbered inodes are reserved:

Inode Purpose
1 Bad blocks inode (tracks bad sectors)
2 Root directory
3–10 Reserved for other kernel structures
11 First regular (user) inode

Forensic note: inode 2 (the root directory) is the entry point for all path-based searches. If root directory metadata is intact, the entire directory tree can be traversed from inode 2 regardless of the state of other structures.

Block Allocation

Block Bitmap

Each block group has a block bitmap — one bit per block — stored in a dedicated block. A 1 bit means the block is allocated; a 0 bit means it is free. When a file is deleted, its blocks are marked free in the bitmap, but the data is not overwritten.

Forensic note: free blocks (bit = 0) identified by the block bitmap are the primary targets for data recovery and file carving. The block bitmap from each group must be read to identify which blocks contain potentially recoverable content across the entire volume.

Inode Bitmap

Similarly, each block group has an inode bitmap — one bit per inode. A 0 bit means the inode is unallocated (the corresponding file has been deleted or the inode was never used).

Forensic note: scanning for inode bitmap entries set to 0 where i_dtime is nonzero identifies deleted files whose inodes have not been reused. This is the first step in deleted file recovery.

Directory Entries

Directories in ext are files whose content is a linear list of directory entries (ext2_dir_entry or ext2_dir_entry_2). Each entry contains:

Field Contents
inode Inode number of the file (0 = entry is deleted)
rec_len Length of this directory entry record
name_len Length of the filename
file_type (ext2_dir_entry_2) Type hint (regular file, directory, symlink, etc.)
name Filename (not null-terminated in the structure; length given by name_len)

When a file is deleted from a directory, its entry is not removed from the directory block; instead, the inode field is set to 0 and the rec_len of the preceding entry is increased to skip over it. The filename and original inode number often remain readable in the slack space of the merged entry.

Forensic note: deleted directory entries frequently retain the original filename and inode number. Even if the inode has been reused, the orphaned directory entry can reveal that a file with that name once existed in this directory, potentially linking it to a specific user account or location. Forensic tools like The Sleuth Kit’s fls display both live and deleted directory entries.

htree Directory Indexing (ext3/4)

Large directories in ext3 and ext4 use an HTree (a B-tree variant indexed by filename hash) to speed up lookups. The HTree index is stored in directory data blocks. When entries are deleted, index nodes may become partially stale but are not immediately removed. HTree leaf nodes contain the same dir_entry structures as linear directories and are subject to the same deleted-entry analysis.

Block Addressing

ext2/3: Indirect Blocks

Small files use direct block pointers (12 per inode), pointing directly to data blocks. Larger files use:

Forensic note: if an inode is deleted and its direct pointers are still intact, data recovery is straightforward. If indirect blocks have been reallocated, some or all of the file’s blocks become unlocatable through the inode — falling back to file carving.

ext4: Extents

ext4 replaces the indirect block scheme with extents — a more efficient structure that records a contiguous run of blocks as a single (start block, length) pair. Up to four extents fit directly in the inode; larger files use an extent tree.

Forensic note: extent-based addressing makes deleted file recovery easier when the inode is intact, because a small number of extents describes the file’s entire layout. However, ext4 also introduces delayed allocation (blocks are not assigned until data is flushed to disk), which means very recently created files may not have block assignments recorded even if the data is in the page cache — though this is only relevant on a live system.

Journaling

ext3 and ext4 add a journal — a write-ahead log of pending metadata (and optionally data) changes. The journal allows the file system to recover from power failures or crashes without a full fsck.

The journal is stored as a file (typically inode 8) in the file system itself. Three journaling modes:

Mode What is journaled Forensic implication
writeback Metadata only; data may be written before/after Data blocks may appear inconsistently ordered
ordered (default) Metadata only; data written before metadata committed Safer consistency; most common
journal Both metadata and data Data written twice; journal contains full file content

Forensic note: in journal mode, the journal file itself may contain previous versions of recently overwritten file data. The journal has a fixed circular buffer size (default 128 MB); older entries are overwritten as new transactions are committed. On a live or recently used system, examining the journal can reveal the contents of recently deleted or modified files. Tools like jcat (The Sleuth Kit) can extract journal contents.

File Deletion in ext

When unlink() is called on the last hard link to a file:

  1. The directory entry’s inode field is set to 0; rec_len of the preceding entry absorbs the freed slot
  2. i_links_count in the inode is decremented to 0
  3. i_dtime in the inode is set to the current time
  4. The inode is marked free in the inode bitmap
  5. Each data block is marked free in the block bitmap

The data blocks are not overwritten. The inode structure is not erased — only its allocation status changes. Until the inode and its blocks are reallocated, the complete file (content, timestamps, ownership, permissions, and deletion time) is recoverable.

What ext4 erases on deletion: ext4 by default zeroes the block pointers in the inode when a file is deleted (unlike ext2/3, which left them intact). This was a deliberate change to prevent recovery of orphaned blocks after fsck. The effect on forensics:

Forensic note: on ext4, the journal in ordered mode records which blocks were associated with an inode at transaction commit time. If the transaction that deleted the file is still in the journal, the original block pointers can be reconstructed from it, enabling direct recovery even after ext4’s zeroing behavior.

Deleted File Recovery

When the Inode is Intact (ext2/3)

  1. Scan the inode bitmap for entries with i_links_count == 0 and i_dtime != 0
  2. For each such inode, follow the direct and indirect block pointers to recover content
  3. Recover the filename from orphaned directory entries that still reference the inode number

Tools: The Sleuth Kit (ils, icat, fls), Autopsy, extundelete, ext4magic.

When the Inode is Intact (ext4)

The zeroed block pointers make step 2 impossible via the inode alone. Options:

When the Inode Has Been Reallocated

Fall back to file carving against the free blocks identified by the block bitmap.

Extended attributes (xattrs) store additional metadata: SELinux security labels, POSIX ACLs, and arbitrary application-defined attributes. They are stored either in a dedicated block (referenced from the inode) or inline within the inode’s extra space (ext4). Their forensic value is limited but can include security context labels that reveal what role a file was meant to play.

Symbolic links whose target path fits within 60 bytes store the path directly in the inode’s block pointer array rather than in a data block. This means symlinks that short leave no data block allocation and are entirely described by the inode. Recovering deleted symlinks is therefore trivial if the inode is intact.

Forensically Significant Locations

Location Contents Forensic Value
Superblock (group 0 + backups) File system parameters and timestamps Creation time, last mount time, UUID
Inode table (unallocated entries) Deleted file metadata Timestamps, ownership, size, deletion time, block pointers (ext2/3)
Directory data blocks Live and deleted directory entries Filename-to-inode mapping; deleted entries retain names
Journal (inode 8) Recent metadata (and optionally data) transactions Reconstructs deleted file block pointers in ext4; may contain previous file versions
Block bitmap free regions Unallocated data blocks Targets for file carving and slack space analysis
/lost+found Orphaned inodes reconnected by fsck Files that existed but had no directory entry; may be evidence of a crash mid-delete or deliberate directory manipulation

Tools

Tool Purpose
debugfs Interactive ext2/3/4 explorer; examine inodes, blocks, superblock; can undelete in ext2/3
dumpe2fs Print superblock and group descriptor information
e2fsck -n Read-only consistency check; identifies structural anomalies without modifying the image
The Sleuth Kit (fls, ils, icat, fsstat) Forensic file system analysis; displays deleted entries, extracts inode content
Autopsy GUI frontend to The Sleuth Kit
extundelete Recovers deleted files from ext3/4 using journal analysis
ext4magic Advanced deleted file recovery for ext4; reconstructs block lists from journal
foremost / scalpel File carving tools for use against free block regions

Further Reading