Ext4文件系統解析
--以cache.img爲例,基於32位的文件系統
目錄
Cache文件結構:
可見ext4文件系統cache中包含. .. hello lost+found hello_world.ko , 其中有四個Directory Entries
和一個Regular file
Cache文件系統結構
總體結構
Group 0 Padding |
ext4 Super Block |
Group Descriptors |
Reserved GDT Blocks |
Data Block Bitmap |
inode Bitmap |
inode Table |
Data Blocks |
1024 bytes |
1 block |
many blocks |
many blocks |
1 block |
1 block |
many blocks |
many more blocks |
超級塊:
Offset |
Size |
Name |
Description |
||||||||||
0x0 |
__le32 |
s_inodes_count |
Total inode count. 0x2000=8192 |
||||||||||
0x4 |
__le32 |
s_blocks_count_lo |
Total block count. 0x8000=32768 |
||||||||||
0x8 |
__le32 |
s_r_blocks_count_lo |
This number of blocks can only be allocated by the super-user. |
||||||||||
0xC |
__le32 |
s_free_blocks_count_lo |
Free block count. 0x79f0=31216 |
||||||||||
0x10 |
__le32 |
s_free_inodes_count |
Free inode count. 0x1ff2=8178 |
||||||||||
0x14 |
__le32 |
s_first_data_block |
First data block. This must be at least 1 for 1k-block filesystems and is typically 0 for all other block sizes. |
||||||||||
0x18 |
__le32 |
s_log_block_size |
Block size is 2 ^ (10 + s_log_block_size).0x2=2^(10+2)=4096 |
||||||||||
0x1C |
__le32 |
s_log_cluster_size |
Cluster size is (2 ^ s_log_cluster_size) blocks if bigalloc is enabled, zero otherwise. |
||||||||||
0x20 |
__le32 |
s_blocks_per_group |
Blocks per group. 0x8000=32768 |
||||||||||
0x24 |
__le32 |
s_clusters_per_group |
Clusters per group, if bigalloc is enabled. |
||||||||||
0x28 |
__le32 |
s_inodes_per_group |
Inodes per group. 0x2000=8192 |
||||||||||
0x2C |
__le32 |
s_mtime |
Mount time, in seconds since the epoch. 0x573bc363 |
||||||||||
0x30 |
__le32 |
s_wtime |
Write time, in seconds since the epoch. 0x573bc363 |
||||||||||
0x34 |
__le16 |
s_mnt_count |
Number of mounts since the last fsck. 0x06 |
||||||||||
0x36 |
__le16 |
s_max_mnt_count |
Number of mounts beyond which a fsck is needed. 0xffff |
||||||||||
0x38 |
__le16 |
s_magic |
Magic signature, 0xEF53 0xEF53 |
||||||||||
0x3A |
__le16 |
s_state |
File system state. Valid values are:
|
||||||||||
0x3C |
__le16 |
s_errors |
Behaviour when detecting errors. One of:
|
||||||||||
0x3E |
__le16 |
s_minor_rev_level |
Minor revision level. 0x0 |
||||||||||
0x40 |
__le32 |
s_lastcheck |
Time of last check, in seconds since the epoch. 0x0 |
||||||||||
0x44 |
__le32 |
s_checkinterval |
Maximum time between checks, in seconds. 0x0 |
||||||||||
0x48 |
__le32 |
s_creator_os |
OS. One of:
|
||||||||||
0x4C |
__le32 |
s_rev_level |
Revision level. One of:
|
||||||||||
0x50 |
__le16 |
s_def_resuid |
Default uid for reserved blocks. |
||||||||||
0x52 |
__le16 |
s_def_resgid |
Default gid for reserved blocks. |
||||||||||
These fields are for EXT4_DYNAMIC_REV superblocks only. Note: the difference between the compatible feature set and the incompatible feature set is that if there is a bit set in the incompatible feature set that the kernel doesn't know about, it should refuse to mount the filesystem. e2fsck's requirements are more strict; if it doesn't know about a feature in either the compatible or incompatible feature set, it must abort and not try to meddle with things it doesn't understand... |
|||||||||||||
0x54 |
__le32 |
s_first_ino |
First non-reserved inode. 0x0B |
||||||||||
0x58 |
__le16 |
s_inode_size |
Size of inode structure, in bytes. 0x100 |
||||||||||
0x5A |
__le16 |
s_block_group_nr |
Block group # of this superblock. 0x0 |
塊組描述符表:
Offset |
Size |
Name |
Description |
||||||
0x0 |
__le32 |
bg_block_bitmap_lo |
Lower 32-bits of location of block bitmap. 0x09 |
||||||
0x4 |
__le32 |
bg_inode_bitmap_lo |
Lower 32-bits of location of inode bitmap. 0x0a |
||||||
0x8 |
__le32 |
bg_inode_table_lo |
Lower 32-bits of location of inode table. 0x0b |
||||||
0xC |
__le16 |
bg_free_blocks_count_lo |
Lower 16-bits of free block count. 0x79f0 |
||||||
0xE |
__le16 |
bg_free_inodes_count_lo |
Lower 16-bits of free inode count. 0x1ff2 |
||||||
0x10 |
__le16 |
bg_used_dirs_count_lo |
Lower 16-bits of directory count. 0x03 |
||||||
0x12 |
__le16 |
bg_flags |
Block group flags. Any of:
|
||||||
0x14 |
__le32 |
bg_exclude_bitmap_lo |
Lower 32-bits of location of snapshot exclusion bitmap. |
||||||
0x18 |
__le16 |
bg_block_bitmap_csum_lo |
Lower 16-bits of the block bitmap checksum. |
||||||
0x1A |
__le16 |
bg_inode_bitmap_csum_lo |
Lower 16-bits of the inode bitmap checksum. |
||||||
0x1C |
__le16 |
bg_itable_unused_lo |
Lower 16-bits of unused inode count. If set, we needn't scan past the(sb.s_inodes_per_group - gdt.bg_itable_unused)th entry in the inode table for this group. |
||||||
0x1E |
__le16 |
bg_checksum |
Group descriptor checksum; crc16(sb_uuid+group+desc) if the RO_COMPAT_GDT_CSUM feature is set, or crc32c(sb_uuid+group_desc) & 0xFFFF if the RO_COMPAT_METADATA_CSUM feature is set. |
||||||
These fields only exist if the 64bit feature is enabled and s_desc_size > 32. |
|||||||||
0x20 |
__le32 |
bg_block_bitmap_hi |
Upper 32-bits of location of block bitmap. |
||||||
0x24 |
__le32 |
bg_inode_bitmap_hi |
Upper 32-bits of location of inodes bitmap. |
||||||
0x28 |
__le32 |
bg_inode_table_hi |
Upper 32-bits of location of inodes table. |
||||||
0x2C |
__le16 |
bg_free_blocks_count_hi |
Upper 16-bits of free block count. |
||||||
0x2E |
__le16 |
bg_free_inodes_count_hi |
Upper 16-bits of free inode count. |
||||||
0x30 |
__le16 |
bg_used_dirs_count_hi |
Upper 16-bits of directory count. |
||||||
0x32 |
__le16 |
bg_itable_unused_hi |
Upper 16-bits of unused inode count. |
||||||
0x34 |
__le32 |
bg_exclude_bitmap_hi |
Upper 32-bits of location of snapshot exclusion bitmap. |
||||||
0x38 |
__le16 |
bg_block_bitmap_csum_hi |
Upper 16-bits of the block bitmap checksum. |
||||||
0x3A |
__le16 |
bg_inode_bitmap_csum_hi |
Upper 16-bits of the inode bitmap checksum. |
||||||
0x3C |
__u32 |
bg_reserved |
Padding to 64 bytes. |
Inode talble:
根據塊組描述符中的bg_inode_table_lo關鍵字可以獲取到inodetable的位置
首先查看 .目錄的table表:bg_inode_table_lo*block_size+(2-1)*inode_size.(.目錄的inode_number=2)
位置 |
值 |
名稱 |
描述 |
0x0 |
__le16:0x41ed |
i_mode |
文件模式 |
0x2 |
__le16:0x0000 |
i_uid |
所有者UID. |
0x4 |
__le32:0x00001000 |
i_size_lo |
文件大小. |
0x8 |
__le32:0x00000000 |
i_atime |
讀取時間. |
0xC |
__le32:0x57395adc |
i_ctime |
Inode修改時間 |
0x10 |
__le32:0x57395adc |
i_mtime |
文件修改時間. |
0x14 |
__le32:0x00000000 |
i_dtime |
刪除時間 |
0x18 |
__le16:0x0000 |
i_gid |
GID. |
0x1A |
__le16:0x0004 |
i_links_count |
硬鏈接計數. |
0x1C |
__le32:0x00000008 |
i_blocks_lo |
塊計數(512字節) |
0x20 |
__le32:0x00080080 |
i_flags |
文件標識(ext4使用extent需要標記0x80000) |
... |
|||
0x28 |
__le32 |
i_block[EXT4_N_BLOCKS=15] |
塊映射(ext2/3)或區段樹(ext4) |
... |
Extern樹
由於extern樹使能,因此需要查看extern樹結構體:
從40-99這60個字節過去是用作塊映射的,如今用作存儲extent信息。extent結構體有12字節的大小,反應快的同學馬上會說,那麼一個inode可以存放最多5個extent。然而這是不對的,因爲前12個字節(40-51)被段頭(extentheader)所佔據,所以,一個inode中的區段數最多隻能是4。
首先看段頭:
偏移 |
大小 |
名稱 |
描述 |
0x0 |
__le16:0xf30a |
eh_magic |
幻數magic number, 0xF30A. |
0x2 |
__le16:0x01 |
eh_entries |
區段數. |
0x4 |
__le16:0x03 |
eh_max |
最大的區段數. |
0x6 |
__le16:0x0000 |
eh_depth |
段節點在段樹中的深度。0則表示爲葉子節點,指向數據塊;否則指向其它段節點。 |
0x8 |
__le32 |
eh_generation |
暫不討論 |
內部節點:
偏移 |
大小 |
名稱 |
描述 |
0x0 |
__le32 |
ei_block |
邏輯塊號. |
0x4 |
__le32 |
ei_leaf_lo |
區段樹中下一層的區段節點塊地址(低32位),可以指向葉子節點或者內部節點。 |
0x8 |
__le16 |
ei_leaf_hi |
上一欄的高16位地址 |
0xA |
__u16 |
ei_unused |
未使用 |
葉子節點:
偏移 |
大小 |
名稱 |
描述 |
0x0 |
__le32:0x00000000 |
ee_block |
此區段的第一個塊號,起始塊號 |
0x4 |
__le16:0x0001 |
ee_len |
區段內包含的塊數. |
0x6 |
__le16:0x0000 |
ee_start_hi |
此區段所指向的塊號(高16位) |
0x8 |
__le32:0x0000060c |
ee_start_lo |
此區段所指向的塊號(低32位 |
Directory Entries
根據extern tree葉節點所指向的塊號:0x60c*0x1000=0x60c000
Directory Entries結構體
.:
Offset |
Size |
Name |
Description |
||||||||||||||||
0x0 |
__le32:0x00000002 |
inode |
Number of the inode |
||||||||||||||||
0x4 |
__le16:0x000C |
rec_len |
Length of this directory entry. |
||||||||||||||||
0x6 |
__u8:0x01 |
name_len |
Length of the file name. |
||||||||||||||||
0x7 |
__u8:0x02 |
file_type |
File type code, one of:
|
||||||||||||||||
0x8 |
Char:0x2E=. |
name[EXT4_NAME_LEN] |
File name. |
lost+found:
Offset |
Size |
Name |
Description |
||||||||||||||||
0x0 |
__le32:0x0000000B |
inode |
Number of the inode |
||||||||||||||||
0x4 |
__le16:0x0014 |
rec_len |
Length of this directory entry. |
||||||||||||||||
0x6 |
__u8:0x0a |
name_len |
Length of the file name. |
||||||||||||||||
0x7 |
__u8:0x02 |
file_type |
File type code, one of:
|
||||||||||||||||
0x8 |
Char:lost+found |
name[EXT4_NAME_LEN] |
File name. |
hello_world.ko
Offset |
Size |
Name |
Description |
||||||||||||||||
0x0 |
__le32:0x0000000C |
inode |
Number of the inode |
||||||||||||||||
0x4 |
__le16:0x0018 |
rec_len |
Length of this directory entry. |
||||||||||||||||
0x6 |
__u8:0x0e |
name_len |
Length of the file name. |
||||||||||||||||
0x7 |
__u8:0x01 |
file_type |
File type code, one of:
|
||||||||||||||||
0x8 |
Char:hello_world.ko |
name[EXT4_NAME_LEN] |
File name. |
反查inode table尋找文件具體位置
根據hello_world.ko的inode的節點號,再去inode table中反查,0xB000+(0xC-1)*0x100
inode table:
位置 |
值 |
名稱 |
描述 |
0x0 |
__le16:0x81ff |
i_mode |
文件模式 |
0x2 |
__le16:0x0000 |
i_uid |
所有者UID. |
0x4 |
__le32:0x00000a80= 2688 |
i_size_lo |
文件大小.
|
0x8 |
__le32:0x572c42ae |
i_atime |
讀取時間. |
0xC |
__le32:0x572c42c2 |
i_ctime |
Inode修改時間 |
0x10 |
__le32:0x572c42ae |
i_mtime |
文件修改時間. |
0x14 |
__le32:0x00000000 |
i_dtime |
刪除時間 |
0x18 |
__le16:0x0000 |
i_gid |
GID. |
0x1A |
__le16:0x0001 |
i_links_count |
硬鏈接計數. |
0x1C |
__le32:0x00000008 |
i_blocks_lo |
塊計數(512字節) |
0x20 |
__le32:0x00080080 |
i_flags |
文件標識(ext4使用extent需要標記0x80000) |
... |
|||
0x28 |
__le32 |
i_block[EXT4_N_BLOCKS=15] |
塊映射(ext2/3)或區段樹(ext4) |
... |
同樣extern樹使能了,所以需要查看extern樹
首先看段頭:
偏移 |
大小 |
名稱 |
描述 |
0x0 |
__le16:0xf30a |
eh_magic |
幻數magic number, 0xF30A. |
0x2 |
__le16:0x01 |
eh_entries |
區段數. |
0x4 |
__le16:0x04 |
eh_max |
最大的區段數. |
0x6 |
__le16:0x0000 |
eh_depth |
段節點在段樹中的深度。0則表示爲葉子節點,指向數據塊;否則指向其它段節點。 |
0x8 |
__le32 |
eh_generation |
暫不討論 |
內部節點:
偏移 |
大小 |
名稱 |
描述 |
0x0 |
__le32 |
ei_block |
邏輯塊號. |
0x4 |
__le32 |
ei_leaf_lo |
區段樹中下一層的區段節點塊地址(低32位),可以指向葉子節點或者內部節點。 |
0x8 |
__le16 |
ei_leaf_hi |
上一欄的高16位地址 |
0xA |
__u16 |
ei_unused |
未使用 |
葉子節點:
偏移 |
大小 |
名稱 |
描述 |
0x0 |
__le32:0x00000000 |
ee_block |
此區段的第一個塊號,起始塊號 |
0x4 |
__le16:0x0001 |
ee_len |
區段內包含的塊數. |
0x6 |
__le16:0x0000 |
ee_start_hi |
此區段所指向的塊號(高16位) |
0x8 |
__le32:0x0000060E |
ee_start_lo |
此區段所指向的塊號(低32位 |
根據extern tree葉節點所指向的塊號:0x60E*0x1000=0x60E000開始的block,數量爲1個block
至此一個文件就找到了,就可以根據分區信息計算出位於emmc的block的偏移和數量,然後將文件讀出即可
綜上,其實我們再執行某個分區掛載的時候mount,其實就是將系統的super_block,block_group_des和當前目錄的inode表讀取出來去初始化分配好的結構體。當你讀或者寫一個文件的時候再去更新這些結構體,同時根據結構體的信息去找到你要讀或者寫的文件位於存儲外設的硬件的block的位置,並更新文件內容
幾個重要的結構體關係圖: