In the first block of the filesystem, the first 1024 bytes are left for the installation of boot sectors and other oddities. And the next 1024 bytes are used for the superblock (struct ext4_super_block). The remaining 2048 bytes in the first block remain unused (a block has a size of 4096 bytes).
The SuperBlock
The superblock itself is one of the most important structures in ext4 and the beginning of any analysis of the file system. A superblock describes the filesystem and tells the operating system where to find various elements (inodes, etc.). It is the master metadata structure for the ext4 filesystem, analogous to the BIOS parameter block in the FAT and NTFS file systems in purpose. It records various information about the layout, size, and enabled features of the file system. Originally, the superblock and group
descriptors were replicated in every block group, with those
located in block group 0 designated as the primary copies.
This is no longer common practice due to the sparse feature option. This option only replicates the file system superblock and group
descriptors in a fraction of the block groups. The sparse feature flag (sparse_super) is set by default and will store redundant copies of superblocks and group descriptors in block group 0, and then in block group 3x, 5x, and 7x. Another feature flag that organizes superblocks is sparse_super2. If set, the file system will only contain two superblock backups. The backup superblocks and backup group descriptors are never updated by the kernel. They will get updated only if any fundamental parameters of the filesystem are amended, for example, resizing of the filesystem.
![]() |
| Figure 1: Byte-level view of an ext4 Superblock |
The table below explains the data structure of the ext4 file system superblock.
|
Offset |
Size (bytes) |
Name |
Description |
|
0x00 |
4 |
s_inodes_count |
The total number of inodes in the file system. |
|
0x04 |
4 |
s_blocks_count_lo |
The total number of blocks in the file system. |
|
0x08 |
4 |
s_r_blocks_count_lo |
Number of blocks reserved to prevent file system from filling up. |
|
0x0C |
4 |
s_free_blocks_count_lo |
Number of unallocated blocks. |
|
0x10 |
4 |
s_free_inodes_count |
Number of unallocated inodes. |
|
0x14 |
4 |
s_first_data_block |
Block where block group 0 starts. |
|
0x18 |
4 |
s_log_block_size |
Block size (saved as the number of places to shift 1,024 to the left). This is given by the formula 2(10+log_block_size). |
|
0x1C |
4 |
s_log_cluster_size |
Allocation cluster size. |
|
0x20 |
4 |
s_blocks_per_group |
Number of blocks in each block group. |
|
0x24 |
4 |
s_clusters_per_group |
Clusters per group. |
|
0x28 |
4 |
s_inodes_per_group |
The number of inodes in each block group. |
|
0x2C |
4 |
s_mtime |
The UNIX time at which the file system was last mounted. |
|
0x30 |
4 |
s_wtime |
The UNIX time at which the file system was last written. |
|
0x34 |
2 |
s_mnt_count |
The number of mounts since the last file system check (fsck). |
|
0x36 |
2 |
s_max_mnt_count |
Number of mounts beyond which a fsck is needed. |
|
0x38 |
2 |
s_magic |
Magic signature, 0xEF53. |
|
0x3A |
2 |
s_state |
The current state of the file system. Values: 0x0001 → Clean; 0x0002 → Errors; and 0x0004 → Orphan inodes are being recovered. |
|
0x3C |
2 |
s_errors |
Error handling method. Values: 1 → Continue; 2 → Remount read-only; and 3 → Panic. |
|
0x3E |
2 |
s_minor_rev_level |
Minor revision level of the file system. |
|
0x40 |
4 |
s_lastcheck |
UNIX time representing the last file system check. |
|
0x44 |
4 |
s_checkinterval |
Maximum time between checks, in seconds. |
|
0x48 |
4 |
s_creator_os |
Identifier of the OS that created the file system. Values include: 0 → Linux; 1 → GNU Hurd; 2 → Masix; 3 → Free BSD; and 4 → Lites. |
|
0x4C |
4 |
s_rev_level |
Major revision level of the file system. Values include:
A dynamic revision refers to the modern, flexible format of the Linux file system that allows for advanced features and backward compatibility with ext2/ext3. Unlike the old, fixed-format "rev0" file systems, the dynamic revision allows for the dynamic allocation of inodes and file system features to be toggled on or off. It allows for features to be added, such as 64-bit support, extents, and flexible block groups (flex_bg), which improve performance and scalability.
|
|
0x50 |
2 |
s_def_resuid |
The UID that can use reserved blocks (default is 0, i.e., root). |
|
0x52 |
2 |
s_def_resgid |
The GID that can use reserved blocks (default is 0). |
|
0x54 |
4 |
s_first_ino |
First non-reserved inode in file system. |
|
0x58 |
2 |
s_inode_size |
Size of inode structure, in bytes. |
|
0x5A |
2 |
s_block_group_nr |
Block group that this superblock is a part of (if backup copy) |
|
0x5C |
4 |
s_feature_compat |
Compatible feature flags. Kernel can still mount with read/write support even if it does not understand one of the flags in this 32-bit field. The compatible features flag can have the following values:
|
|
0x60 |
4 |
s_feature_incompat |
Incompatible feature flags. Incompatible features are those that could lead to data corruption or misinterpretation when a filesystem is mounted by a system that doesn’t support them, even when mounted read-only. If the file system driver does not support these features, then the file system should not be mounted. The incompatible features flag can have the following values:
|
|
0x64 |
4 |
s_feature_ro_compat |
Read-only compatible feature flags. If the file system driver does not support these features, then the file system should be mounted as read-only. The read-only features flag can have the following values:
|
|
0x68 |
16 |
s_uuid[16] |
128-bit UUID for volume. |
|
0x78 |
16 |
s_volume_name[16] |
Volume label. |
|
0x88 |
16 |
s_last_mounted[64] |
Directory where file system was last mounted. |
|
0xC8 |
4 |
s_algorithm_usage_bitmap |
Algorithm usage bitmap. |
|
Performance hints: Directory pre-allocation should only happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on. |
|||
|
0xCC |
1 |
s_prealloc_blocks |
Number of blocks to pre-allocate for files. |
|
0xCD |
1 |
s_prealloc_dir_blocks |
Number of blocks to pre-allocate for directories. |
|
0xCE |
2 |
s_reserved_gdt_blocks |
Number of reserved GDT entries for future file system expansion. |
|
Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL is set. |
|||
|
0xD0 |
16 |
s_journal_uuid[16] |
UUID of journal superblock. |
|
0xE0 |
4 |
s_journal_inum. |
inode number of journal file. |
|
0xE4 |
4 |
s_journal_dev |
Device number of journal file, if the external journal feature flag is set. |
|
0xE8 |
4 |
s_last_orphan |
Start of list of orphaned inodes to delete. |
|
0xEC |
16 |
_hash_seed[4] |
HTREE hash seed. |
|
0xFC |
|
|
Default hash algorithm to use for directory hashes. Values include:
|
|
0xFD |
1 |
s_jnl_backup_type |
? |
|
0xFE |
2 |
s_desc_size |
Size of group descriptors, in bytes, if the 64bit incompat feature flag is set. |
|
0x100 |
4 |
s_default_mount_opts |
Default mount options. Values include:
|
|
0x104 |
4 |
s_first_meta_bg |
First metablock block group, if the meta_bg feature is enabled. |
|
0x108 |
4 |
s_mkfs_time |
When the file system was created, in seconds since the epoch. |
|
0x10C |
68 |
s_jnl_blocks[17] |
Backup copy of the first 68 bytes of the journal inode. |
|
64-bit support valid if EXT4_FEATURE_COMPAT_64BIT |
|||
|
0x150 |
4 |
s_blocks_count_hi |
High 32 bits of the block count. |
|
0x154 |
4 |
s_r_blocks_count_hi |
High 32 bits of the reserved block count. |
|
0x158 |
4 |
s_free_blocks_count_hi |
High 32 bits of the free block count. |
|
0x15C |
2 |
s_min_extra_isize |
All inodes have at least # bytes. |
|
0x15E |
2 |
s_want_extra_isize |
New inodes should reserve # bytes. |
|
0x160 |
4 |
s_flags |
Miscellaneous flags. Values include:
|
|
0x164 |
2 |
s_raid_stride |
RAID stride. This is the number of logical blocks read from or written to the disk before moving to the next disk. This affects the placement of filesystem metadata, which will hopefully make RAID storage faster. |
|
0x166 |
2 |
s_mmp_interval |
Number of seconds to wait in multi-mount prevention (MMP) checking. In theory, MMP is a mechanism to record in the superblock which host and device have mounted the filesystem, in order to prevent multiple mounts. This feature does not seem to be implemented. |
|
0x168 |
8 |
s_mmp_block |
Block number for multi-mount protection data |
|
0x170 |
4 |
s_raid_stripe_width |
RAID stripe width. This is the number of logical blocks read from or written to the disk before coming back to the current disk. This is used by the block allocator to try to reduce the number of read-modify-write operations in a RAID5/6. |
|
0x174 |
1 |
s_log_groups_per_flex |
Size of a flexible block group is 2s_log_groups_per_flex. |
|
0x175 |
1 |
s_reserved_char_pad |
|
|
0x176 |
2 |
s_reserved_pad |
|
|
0x178 |
8 |
s_kbytes_written |
Number of KiB written to this filesystem over its lifetime. |
|
0x180 |
4 |
s_snapshot_inum |
inode number of active snapshot. |
|
0x184 |
4 |
s_snapshot_id |
Sequential ID of active snapshot. |
|
0x188 |
8 |
s_snapshot_r_blocks_count |
Number of blocks reserved for active snapshot's future use. |
|
0x190 |
4 |
s_snapshot_list |
node number of the head of the on-disk snapshot list. |
|
0x194 |
4 |
s_error_count |
Number of errors seen. |
|
0x198 |
4 |
s_first_error_time |
First time an error happened, in seconds since the epoch. |
|
0x19C |
4 |
s_first_error_ino |
inode involved in first error. |
|
0x1A0 |
8 |
s_first_error_block |
Number of block involved in first error. |
|
0x1A8 |
32 |
s_first_error_func[32] |
Name of function where the error happened. |
|
0x1C8 |
4 |
s_first_error_line |
Line number where error happened. |
|
0x1CC |
4 |
s_last_error_time |
Time of most recent error, in seconds since the epoch. |
|
0x1D0 |
4 |
s_last_error_ino |
inode involved in most recent error. |
|
0x1D4 |
4 |
s_last_error_line |
Line number where most recent error happened. |
|
0x1D8 |
8 |
s_last_error_block |
Number of block involved in most recent error. |
|
0x1E0 |
32 |
s_last_error_func[32] |
Name of function where the most recent error happened. |
|
0x200 |
64 |
s_mount_opts[64] |
ASCIIZ string of mount options. |
|
0x240 |
4 |
s_usr_quota_inum |
inode for tracking user quota. |
|
0x244 |
4 |
s_grp_quota_inum |
inode for tracking group quota. |
|
0x248 |
4 |
s_overhead_clusters |
overhead blocks/clusters in file system. |
|
0x24C |
8 |
s_backup_bgs[2] |
groups with sparse_super2 Superblocks. |
|
0x254 |
4 |
s_encrypt_algos[4] |
Encryption algorithms in use. Values include:
|
|
0x258 |
16 |
s_encrypt_pw_salt[16] |
Salt used for the string2key algorithm. |
|
0x268 |
4 |
s_lpf_ino |
Location of the lost+found inode. |
|
0x26C |
4 |
s_prj_quota_inum |
inode for tracking project quota. |
|
0x270 |
4 |
s_checksum_seed |
crc32c(uuid), if csum_seed set. |
|
0x274 |
1 |
s_wtime_hi |
|
|
0x275 |
1 |
s_mtime_hi |
|
|
0x276 |
1 |
s_mkfs_time_hi |
|
|
0x277 |
1 |
s_lastcheck_hi |
|
|
0x278 |
1 |
s_first_error_time_hi |
|
|
0x279 |
1 |
s_last_error_time_hi |
|
|
0x27A |
2 |
s_pad[2] |
|
|
0x27C |
2 |
s_encoding |
Filename charset encoding. |
|
0x27E |
2 |
s_encoding_flags |
Filename charset encoding flags. |
|
0x280 |
4 |
s_orphan_file_inum |
|
|
0x284 |
Padding |
s_reserved |
Padding to the end of the block. |
|
0x3FC |
4 |
s_checksum |
crc32c(Superblock). |
Supported Features
The superblock defines the features supported in three different 32 bit fields, viz;
- Byte offset 0x5C compatible (
s_features_compat). - Byte offset 0x60 incompatible (
s_features_incompat). - Byte offset 0x64 read-only compatible (
s_features_ro_compat).
Please refer to the relevant offsets in the above table for detailed explanation. There are a number of reasons why this is relevant to digital forensic investigations.
- First, these features may affect the structure of block groups.
- Second, this in turn affects where data is located.
- Third, features affect how data is stored.
- Fourth, some features might result in a new source of metadata for use in your analysis.
Compatible Features
The reader is admonished to refer to offset 0x5C of the above table for clearer understanding. From a forensic investigation perspective, not all compatible file system features are equally relevant. One feature of particular importance is COMPAT_SPARSE_SUPER2, which is critical for identifying backup superblocks when the primary superblock is damaged, corrupted, or deliberately manipulated. When this flag is enabled, the superblock field s_backup_bgs, located at byte offset 0x24C, specifies the two block groups that contain backup superblocks. Although a single field referencing two block groups may appear counterintuitive, this behavior is explained by the fact that the field is implemented as an array of two 32-bit values.
Another significant feature is COMPAT_HAS_JOURNAL. When set, this flag indicates that the filesystem maintains a journal, enabling potential recovery of metadata and, in some cases, file content from journal transactions. It is important to note that journal storage operates in a circular manner; once the journal reaches capacity, new transactions overwrite older ones, which may limit the availability of historical data.
The COMPAT_EXT_ATTR feature is also forensically relevant, as it allows extended attributes to be stored within inodes. This functionality enables users and applications to associate additional metadata with individual files, which may contain evidentiary value during an investigation.
The COMPAT_RESIZE_INODE feature, despite its non-descriptive name, defines the number of blocks reserved for the extended Group Descriptor Table (GDT). These reserved blocks facilitate future file system expansion. From a forensic standpoint, they are particularly important because mandatory block group structures are located after the reserved GDT blocks. Awareness of this layout can significantly aid manual filesystem reconstruction and recovery efforts.
Finally, certain compatible features are supported by Linux but are not actively utilized in practice. One such example is COMPAT_DIR_PREALLOC, which permits pre-allocation of a specified number of blocks for directories through the s_prealloc_dir_blocks field at superblock byte offset 0xCD. However, this field is currently unused by the Linux kernel.
Incompatible Features
The ext4 file system offers a new feature called a flexible block group (INCOMPAT_FLEX_BG). This feature enhances performance by allowing the combination of multiple block groups into a single logical block group. The block metadata of multiple block groups (i.e., block bitmaps, inode bitmaps, and inode tables) is placed close together as one long run in the first block group of the flexible block group. In ext4, the number of block groups that form a flex group must be a power of two. Within a flex group, the block and inode bitmaps are laid out as contiguous concatenations of the corresponding bitmaps from each constituent block group. As a result, the size of each bitmap structure equals the filesystem block size multiplied by the number of block groups in the flex group. Likewise, the flex group inode table is a contiguous concatenation of the inode tables from the individual block groups. The size of the flex group inode table can be determined by calculating the inode table size per block group and multiplying it by the number of block groups in the flex group. Because these metadata structures are no longer constrained to reside within the block group they manage, ext4 can maintain significantly larger spans of contiguous free data blocks within a flex group, exceeding the 128 MB allocation limit imposed by the block group layout in ext3. The flex group feature does not alter the placement of backup superblocks, group descriptors, or group descriptor growth blocks. Although these structures may still interrupt spans of free contiguous data blocks, such interruptions become less frequent as the number of block groups in the filesystem increases.
The investigator should assume and test that the data block bitmap, inode bitmap, and inode table will exist in the first block group when flex groups are being used. In this case the metadata is near co-located in the beginning of the file system. When flex groups are not used, it will be necessary to parse all superblocks and group descriptors in order to identify all bitmaps and the complete inode table.
Read-only Compatible Feature
As illustrated in offset 0x64 in the table above, filesystems that support large files may enable several read-only compatible features, including RO_COMPAT_LARGE_FILE, which indicates the presence of files exceeding 2 GB in size; RO_COMPAT_BIGALLOC, where extents are allocated in clusters rather than individual blocks; and RO_COMPAT_HUGE_FILE, in which file sizes are represented using logical blocks instead of sectors. Large files are often of particular forensic interest, as they may store multimedia content, filesystem containers, virtual disks, or encrypted data. Consequently, such files warrant careful examination during an investigation.
In earlier file system versions, such as Ext2 and Ext3, the default inode size was 128 bytes. With Ext4, this size was commonly increased to 256 bytes. The RO_COMPAT_EXTRA_ISIZE feature indicates that additional inode metadata is in use, enabling Ext4-specific capabilities, including nanosecond-resolution timestamps and the storage of file creation times. Any remaining inode space beyond the extended inode size remains reserved for extended attributes.
The use of metadata checksums is intended to protect filesystem structures from being interpreted when corruption or unintended modification occurs. However, while checksums can detect certain forms of corruption, they do not inherently prevent malicious alteration of metadata if the corresponding checksum is recomputed to match the modified data.
Another aspect of potential forensic relevance is the presence of filesystem snapshots, which preserve earlier states of the filesystem. Such snapshots can provide valuable historical context during an investigation. At present, however, native snapshot support is not implemented in ext4 on Linux systems.
The RO_COMPAT_VERITY feature may also be of interest to investigators, as it indicates the existence of verity-enabled inodes. These inodes contain read-only data whose integrity can be verified using a Merkle tree–based hashing mechanism. In this approach, file data is divided into blocks, each of which is hashed. The resulting hashes are then combined and rehashed iteratively, forming progressively larger hash units until a single root hash is produced. This final hash represents the entire file and is used to verify the integrity of the read-only content.
Now that we have given the data structures and all the flags, let us look at an actual ext4 superblock as shown below. It is important to note that ext4 record values in little-endian. Hence, appropriate conversion must be made to interpret values correctly.
Bytes 0x00-0x03 (0x004E0000) show us that there are 5,111,808 inodes, and bytes 0x04-0x07 (0x01380000) show that there are 20,447,232 blocks. Bytes 0x14-0x17 show that block 0 is where block group 0 starts. Bytes 0x18-0x1B contain the number of bits to shift the number 1,024 in order to calculate the block size. This value is 0x2. To calculate the block size, take the value for log_block_size, 0x02, and calculate, 2(10+log_block_size) = 2(10+2) = 212 = 4096.
Bytes 0x20-0x23 (0x8000) show that there are 32,768 blocks in each block group. Given the information recorded in bytes 0x04-0x7 and that recorded in bytes 0x20-0x23, we can determine the total number of block groups in the file system as follows:
Number of block groups = Total number of blocks/Number of blocks in each block group
= 20447232/32768
= 642
Bytes 0x28-0x2B (0x2000) show that there are 8,192 inodes per group. Bytes 0x4C-0x4F show that we have the dynamic version of the file system, so standard ext4 features apply. We see in bytes 0x58-0x5B (0x0100) that the size of each inode is 256 bytes.
Bytes 0x5C-0x5F have the compatible feature flags. The value 0x3C = 0x20 (indexed directories) + 0x10 (has reserved GDT) + 0x8 (support extended attributes) + 0x4 (has journal). Bytes 0x60-0x63 have the incompatible feature flag value of 0x02C2 = 0x200 (flexible block groups) + 0x80 (file system can be 264 blocks) + 0x40 (files uses extents) + 0x2 (directory entries record file type). Bytes 0x64-0x67 contain the read-only compatible feature flag value of 0x046B = 0x400 (checksums are used on metadata) + 0x40 (large inodes) + 0x20 (ext3 32000 subdirectory limit no longer applies) + 0x8 (file space usage is stored in units of inode block sizes [huge file]) + 0x2 (allow storing files larger than 2GB [large files]) + 0x1 (sparse superblocks). Bytes 0xE0-0xE3 show that the journal is located in inode 8.
Understanding the Ext4 superblock is a foundational step in Linux filesystem forensics. As the central metadata structure, the superblock defines how the filesystem is laid out, how its components are interpreted, and whether meaningful analysis is even possible. For forensic practitioners, the superblock is more than just structural metadata — it provides critical context such as filesystem size, creation details, feature flags, and timestamps that can directly support timeline reconstruction, system attribution, and data recovery efforts.
In scenarios involving filesystem corruption, deliberate tampering, or partial disk images, knowledge of backup superblocks and their placement becomes especially valuable, often making the difference between a failed analysis and a successful recovery. By correctly interpreting superblock fields, investigators can validate filesystem integrity, locate key structures like inode tables and block groups, and make informed decisions when carving or reconstructing evidence.
This examination of the Ext4 superblock serves as a starting point for deeper filesystem analysis. In subsequent posts, attention can be shifted to other critical structures such as block group descriptors, inode tables, journals, and extents — all of which build upon the information defined by the superblock and further shape the forensic story a filesystem can tell.



Post a Comment