昨天追蹤EXT4文件系統的過程中出了點問題,就是找不到文件,於是試了一下追蹤FAT32文件系統的,成功之后有了點信心,今天繼續嗑EXT4文件系統,終於找到啦,記錄一下。
- 操作系統:linux(centos 6.5)
- 文件系統:EXT4
- 工具:hexdump,windows自帶計算器
- 參考資源:《數據重現-文件系統原理精解與數據恢復最佳實踐》(馬林 著)
《基於EXT4文件系統的數據恢復方法研究》(徐國天)
題為《Ext4文件系統架構分析》的系列博客
題為《 深入理解ext4(一)----extent區段》的博客
題為《ext4的Extent解析》的博客
題為《ext4_ext_find_extent解析》的博客
EXT4文件系統架構(非原創):
補充說明:EXT4文件系統中只有0號塊組的超級塊和塊組描述符表的位置是固定的,其他都不固定。其中,超級塊總是開始於偏移位置1024(字節),占據1024個字節,塊組描述符表緊隨超級塊后面,占用的大小是不定。
步驟:
1、查看文件系統基本情況,新建子目錄和文件
可以看到掛載在/boot目錄下的文件系統類型是EXT4,因此在改目錄下新建子目錄及文件:
文件內容為:“This test is belong to Boot folder!”文件基本信息如下:
2、查看超級塊,找到0號塊組起始塊號、塊大小、每塊組所含塊數、每塊組i節點數、第一個非保留i節點、每個i節點大小。
命令:hexdump -s 1024 -n 1024 -C /dev/sda1
查看結果:
首先可以看到0x38-0x39是EXT系列文件系統的簽名標志:“53 ef”
0x14-0x17是0號塊組起始塊號:0x01,說明超級塊前面有一個塊為保留塊,用來存儲引導程序。
0x18-0x1b是塊大小:0x00,這里的值指的是將1024字節左移的位數,移動0位也就是1024字節,移動一位相當於乘以2,就是2048字節。
0x20-0x23是每塊組所含塊數:0x2000(十進制8192)
0x28-0x2b是每塊組所含i節點數:0x07f0(十進制2032)
0x54-0x57是第一個非保留i節點號:0x0b(11),一般為lost+found目錄
0x58-0x59是每個i節點結構的大小:0x80(十進制128),也就是每個i節點表項占用128個字節。
3、查看塊組描述符表,找到塊位圖塊、i節點位圖塊、i節點表起始塊號、塊組目錄數。
命令:hexdump -s 2048 -n 1024 -C /dev/sda1
查看結果:
塊組描述符表中每個塊組使用32個字節來描述,因此第一個32字節描述的就是0號塊組。
0x00-0x04是塊位圖塊起始塊號:0x0104
0x05-0x07是i節點位圖塊起始塊號:0x0114
0x08-0x0b是i節點表起始塊號:0x0124
0x10-0x11是該塊組的目錄數:0x02
這里獲取的起始塊號是邏輯塊號(將文件系統所有的塊從0開始遞增編號),因此在計算偏移量時可以直接乘以每塊字節數(0x400,也就是十進制的1024)
這里補充說明一下:EXT3文件系統的塊位圖塊號、i節點位圖塊和i節點表起始塊號是遞增的,而這里他們三個之間卻是相差一個常量:16。
因為這里使用了一個EXT4新引進的結構:Flexible 塊組(flex_bg)
Flexible 塊組 Flexible 塊組的設計目的是組成更大的邏輯塊組,盡量讓大文件連續,將元數據聚集加快元數據載入。因此它的做法是將幾個塊組的塊位圖塊,i節點位圖塊,i節點表塊放在這個邏輯塊組的第一個塊組中,這樣剩下的塊組中就只存儲了該塊組的超級塊和塊組描述表。 上面的塊組描述表中可以看出這個Flexible 塊組是將16個塊組合成了一個邏輯塊組。 說起Flexible 塊組就要提起元塊組(Media Block Group),因為他和Flexible 塊組是“有你沒我”的關系。Flexible 塊組是移動了塊位圖塊、i節點位圖塊、i節點表塊,元塊組是減少了塊組描述符表的備份,原本塊組描述符表和超級塊一起備份在塊組號為0或者3、5、7的冪的塊組,元塊組是只在一個元塊組的第一、二個塊組和最后一個塊組中備份塊組描述符表,增加元塊組存儲數據的空間。 |
3、從根目錄中找到子目錄
第一步我們提到了第一個非保留i節點號為11,那么前面的10個保留i節點的作用是什么呢(i節點號從1開始編號),這里只說明2號節點是存儲的是根目錄i節點號,因此我們讀取i節點表的2號表項值就可以找到根目錄所在塊號了。
計算i節點表項的偏移量涉及到了塊組描述符表中的i節點表起始塊號:0x0124。
某i節點表項起始字節=i節點起始塊號*每塊所占字節數+(該i節點號-1)*每個i節點表項所占字節數
0x0124*0x400+(0x02-0x01)*0x80=0x49080
下面就可以讀取根目錄i節點表項值了。
命令:hexdump -s 0x49080 -n 128 -C /dev/sda1
查看結果:
0xa8-0xd7是12個直接塊指針,其中四個字節為一個單位,表示一個塊號。
(這里要解釋的是:EXt4文件系統將12個直接塊指針、1個一級間接塊指針、1個二級間接塊指針、1個三級間接塊指針,一共60個字節用extent結構來替換,但前提是偏移0xa0-0xa3處的標志位置為“00 00 08 00”,而這里全為0,則證明沒有使用extent結構,因此依舊按照塊指針形式查找根目錄)
圖中可以看出根目錄只占用了一個塊,塊號為:0x1104
根目錄的起始偏移字節為=根目錄所在塊號*每塊所占字節數
則根目錄的起始偏移字節:0x1104*0x400=0x441000
使用命令:hexdump -s 0x441000 -n 1024 -C /dev/sda1 查看根目錄內容:
查看/boot目錄下的文件:
可以看到兩者的內容是相符的,說明我們找的沒有錯。根目錄中BOOTDIR的目錄項用黑色底紋標注。
0x6c-0x6f是該文件內容所在i節點號:0x7f01
0x70-0x71是本目錄項長度:0x10(16字節)
0x72是本目錄項名字長度0x07(7個字節)
0x73是本文件類型:0x02(表示目錄)
0x74開始是文件名的ASCCI碼:“42 4f 4f 54 44 49 52 00”
在這一步中與FAT32文件系統的區別有兩個:
一是怎么尋找根目錄。FAT32中根目錄在數據區的開頭,因此我們可以直接去數據區讀取;而EXT4文件系統中,我們需要通過2號i節點表找到根目錄所在的塊號,才能看到根目錄內容,這里就可以看出EXT4文件系統將目錄也看作文件了,因為他的讀取方式和普通文件是一樣的,只不過普通文件需要從目錄中得到i節點號,而根目錄是一開始就定好了i節點號。
二是目錄的大小。FAT32中目錄的大小是固定的(短文件名目錄占32字節,長文件名目錄占多個32字節),所以當文件名過長時,使用了長文件名機制來解決,而EXT4文件系統的目錄項大小是在目錄項中靈活定的。比如這一步中我們查看到的根目錄結果中,開始的12個字節是本目錄項,緊接着12個字節是根目錄項,而我們要找的目標目錄項的長度是16個字節,其中說明部分(i節點號,本目錄項長度字節數,名字長度,文件類型)占用都是一樣的,差就差在文件名部分。但我們也看到文件名后面總有“00”補齊,這是因為目錄項的長度總要是4的倍數,因此不夠時會用0補齊。
4、從i節點表中找到子目錄所在塊號
第三步中我們找到指向子目錄的i節點號為0x7f01,也是邏輯i節點號,因此我們要先找到0x7f01在哪個塊組中:
某i節點所在塊組=該i節點號/每塊組i節點個數
0x7f01/0x7f0=0x10(十進制16)
某i節點所在i節點表號=該i節點號%每塊組i節點個數
0x7f01%0x7f0=0x01
因此0x7f01在16號塊組的i節點表中,在改i節點表的1號表項中。
接下來我們要從塊組描述符表中找到16號塊組的i節點表起始塊號,以便找到子目錄所在塊號。
某塊組在塊組描述符表中偏移字節=塊組描述符表起始字節+塊組號*每塊組描述符表項字節數
2048+16*32=2560字節
我們讀取2560偏移字節開始的32字節:
可以看到:0x08-0x0b為i節點表起始塊號:0x020021
讀取16號塊組i節點表的1號i節點表項值:
這里需要重點注意:0x20-0x23處的標志內容為“00 00 08 00”也就是0x080000,表示使用了extent結構,因此這里文件子目錄的搜索就需要按照extent結構來讀取0x28-0x63這60個字節的內容。
有個疑問:EXT4文件系統什么時候使用extent結構,查看0號塊組的10個保留i節點時,看到只有8號也就是日志節點啟用了extent結構,其他都沒有使用,而其他塊組中似乎是都默認啟用extent結構,但也發現了例外,因此不能確定EXT4關於啟用extent結構的規定,后續需要注意!
下面介紹一下extent的結構,內容有參考。 每個extent結構占用12個字節,所以每個i節點表項中可以有60/12=5個extent結構,這其中第一個extent作為extent頭(extent header是一個B+樹的描述頭),剩下的4個是extent體(extent body,由於extent樹中的節點有兩種:索引節點和葉子節點,因此當節點為索引節點時(可以從extent header中區分索引節點和葉子節點)extent body存儲的是下一級extent樹節點信息,當節點為葉子節點時extent body 中存儲的是數據塊塊號信息) extent樹結構
extent數據結構 extent header 這里要說明的是:魔數是一個校驗值,只有當校驗結果是0xf30a時B+樹的塊才正確 節點在extent樹中的深度是從葉子節點算起,因此根節點的深度是最深的(看到有人說根節點的最大深度不超過5),當深度為0時表明是葉子節點,那么這個節點就是數據節點,他后面的extent body就是指向的數據塊,存儲數據塊號;當深度大於0,后面的extent body表示索引節點。 extent body 當為索引節點時 當為葉子節點時 補充說明:當extent body表示索引節點時最后最后兩個節點冗余是為了遷就葉子節點。 |
從0x28-0x63是extent結構。
extent頭中說明的信息有:本區段有一個extent body,最大區段個數為4,本段在extent樹中深度為0,是葉子節點。
后面緊着的12個字節是extent body說明的信息有:本區段的第一個塊號是0,本區段含有一個塊,本區段指向的數據塊塊號為0x021002。
該塊的偏移字節:0x8400800
5、從子目錄對應的i節點號得到目標文件塊號
查看結果第4步中子目錄塊內容:
加黑色底紋的是本目錄(“.”)和根目錄(“..”)接下來就是目標文件:BOOTTEXT.txt,他的i節點號為:0x7f04
查看i節點表,找到目標文件的塊號:
該i節點表項的偏移字節為:0x020021*0x400+(0x7f04%0x7f0-1)*0x80=0x8008580
讀取該偏移字節處開始的128字節內容:
同樣目標文件的i節點表項也啟用了extent結構,按照與第4步同樣的方法分析,得帶目標文件所在塊號0x024005(偏移字節為0x9001400)。
接下來讀取該塊內容:
找到啦!和第一步中使用cat命令查看的文件內容一致。