作者簡介: 黃偉亮(Huang weller),畢業於蘇州大學,就職於蘇州博世汽車部件汽車多媒體事業部,從事汽車多媒體娛樂系統的平臺開發工作六年有餘, 接觸Linux 系統近10年。感興趣的方向有Linux系統效能最佳化,多媒體框架, 檔案系統和儲存器件, USB以及虛擬化等。
開篇
筆者一直認為,檔案系統就是構建在塊裝置上的資料庫,檔案名是檢索資料庫中資料的一種手段。原理永遠是簡單的,就像火箭上天只要有足夠大的反向推力裝置就好了,就像空調製冷就是利用汽化吸熱的原理一樣。但實現是複雜的.
筆者十分偏愛ext4這個檔案系統,原因之一是一直和這個檔案系統打交道,曾經一年多的時間沒做別的事情,工作內容全是它。另一個原因,則是它是linux世界使用最廣泛的檔案系統,google的GFS也是構建在ext4之上的。而且ext4著名的maintainer Ted 也在google就職, 社群的maintainer回答問題也很積極和友好.不過,因為是偏愛,其實不需要任何理由.
回顧一下筆者當年的學習過程,簡單概括起來就是,有表入裡,由淺入深。過程也是充滿艱辛. 話說資訊時代,網路有千文, 但看別人的文章, 總不如自己實踐一番來的體會深刻.
但是篇幅和精力所限,本文不做涉及檔案系統技術細節的具體分析,比如jbd2在檔案系統中的具體作用和分析等,本文 只是提供一個分析檔案系統的實踐過程.
那麼, 就拿張SD卡follow me吧.
Ext4 layout
Ext4的layout的詳細內容這裡就略過了, 童鞋們可以到這個頁面去瞭解:
https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inline_Data
一定要看過以後再來看這篇拙文哦! 如果那個連結實在看不下去, 那就看下麵這張圖吧:
在 mkfs.ext4命令格式化完塊裝置以後, ext4的layout 在塊裝置上大致如上圖所描述, 包含了一下資訊:
-
塊裝置被按group 來劃分,每個group有對應的group descriptor
-
Super block在Group 0中, 非group 0中的super block是backup super block
-
Journal block的位置並不是在最後一個group.(本例中,journal block在group 2)
定位檔案系統超級塊superblock
首先, 如果磁碟是DOS的分割槽格式, 那就用fdisk命令檢視一下磁碟的分割槽資訊. 從磁碟的分割槽資訊我們可以得到以下內容:
-
整個磁碟有多大
-
磁碟被分成了多少個分割槽
-
每個分割槽的大小和起始結束的位置
下圖給出了一個例子, 後面的檔案系統分析也是基於該磁碟: /dev/sde1.
下麵這張圖清晰的描述了測試所用的SD卡的分割槽資訊, 對塊裝置/dev/sde來說, 最小的單元為sector, sector size通常為512, SD卡的大小可以透過sector來描述.
那麼, 接下來就要開始找ext4的super block了. ext4的superblock包含一個magic number : __le16 s_magic = 0xEF53, 我們可以用過它來確認superblock.
透過Linux的常用命令dd + hexdump來檢視想要檢視的block device 任意位置的內容.
如下圖所示,dd 命令:
sudo dd if=/dev/sde bs=512 skip=2048 | hexdump -C -n 2048
skip到塊裝置 sde1的offset 2048 x 512B(這裡的512即sector size)位置, hexdump出後面2KB的內容.在0x438位置, 找到了ext4的super block magic word, 同時, 也看到了在使用mkfs.ext4 時, -L引數指定的disk label: “SDE1_weller”
定位journal block 和 inode table
Ext4離開jbd2能活嗎? 可以! 但會變得非常不可靠. 關於jbd2和檔案系統的關係, 會在以後的文章中再做詳述.不過我們要知道journaling block device是通用的journaling layer,為其他檔案系統提供journaling服務.
因為檔案的寫操作和掛載過程和jbd2緊密相關, 所以在分析檔案系統問題的時候, 我們通常需要查詢journal block裡的內容做分析,因此請跟隨下麵的內容來定位journal block.
首先使用dumpe2fs來檢視sde1塊裝置上的ext4檔案系統的資訊,這也是本文唯一使用的ext4原生工具.
如下圖所示可知如下資訊:
-
journal的位置資訊儲存在inode table 的 offset 8 上(inode index = 8)
-
journal的整個大小是16MB
-
journal的長度則是和檔案系統的block size相同, 也是4KB.
-
Inode size 是256B
命令:
sudo dumpe2fs /dev/sde1
那麼, 問題來了,為了找到journal的起始位置, 我們不得不先去找inode index 8所在的位置. 其實,用hexdump直接search journal的magic word也可以找到journal block的起始位置, 但是這是筆者初學時的方法, 現在想來實在太low.
定位inode table
回到sudo dumpe2fs /dev/sde1這條命令的輸出結果上來, 在Group 0的描述中,Inode table的起始block number是65.
已知該檔案系統的block size是 4KB, Inode size 是256B.
那麼inode index 8 在inode table中的 offset 為 (8-1) x 256B = 0x700
使用如下命令dump出來, 擷取0x700開始的256B內容:
命令:
sudo dd if=/dev/sde1 bs=4096 skip=65 | hexdump -Cv -n 2048
由此可知, journal block 的起始位置是0x00010000 x 4KB,也就是block number 為0x10000的block,大小為0x1000 x 4KB(16MB).
如何判斷結果上述的結果準確呢?
Journal block對資料格式也是有定義的, 判斷依據是, /dev/sde1裝置的0x10000 block offset處的資料內容應當是journal的super block, block type為4意為 Journal superblock v2.
來一窺真容:
命令:
sudo dd if=/dev/sde1 bs=4096 skip=65536 | hexdump -Cv -n 2048
定位檔案內容位置
要找到一個檔案內容在磁碟上的位置, 我們需要先瞭解一下檔案在ext4上的建立過程.Data block bitmap和inode bitmap想像記賬員一樣,記錄著data block和inode table的使用情況. Inode table用來描述檔案內容所在的block number 和number of block.
首先我們在塊裝置sde1 mount的目錄下建立一個檔案file1.txt. 然後用 ls -i 命令獲得file1.txt的inode index.
下例中, file1.txt的inode index為12.
inode 12 在inode table中的 offset為 (12-1) x256B = 0xB00. 用下麵的命令dump inode table並且擷取0xB00位置的256B位元組:
命令:
sudo dd if=/dev/sde1 bs=4096 skip=65 | hexdump -Cv -n 4096
由圖可知, 檔案的資料塊在0x00008021的地方, 我們dump出來看看吧, 猜猜我在檔案裡寫了哪些內容呢?
命令:
sudo dd if=/dev/sde1 bs=4096 skip=32801 | hexdump -Cv -n 4096
定位檔案名位置
不知道你會不會好奇,這個檔案是在根目錄下, 那麼檔案名又是存在什麼位置的呢 ? ext4是怎麼知道檔案夾它包含哪些檔案的呢?
首先根目錄也是目錄,目錄在檔案系統裡面的表示和檔案的表示是一樣一樣的.
同樣可以找到根目錄的inode index : ls -i . -a
我們可以看到,當前目錄, 也就是”.”的inode index為 2.
Inode index 2 內容(命令忽略了,可以思考一下命令是怎樣的):
根據inode table描述, 根目錄的資料塊在block offset 0x25, 長度為1個block.
讓我們一起看看block number 為0x25的內容吧, 我們可以看到“.”, “..”, lost+found 和file1.txt 等檔案名. 至於這一塊的資料格式定義, 請童鞋自行學習吧.
命令:
sudo dd if=/dev/sde1 bs=4096 skip=37 | hexdump -Cv -n 4096
新增softlink後的檔案系統分析
在測試目錄下新增一個軟連結檔案,指向檔案file1.txt
用之前的方法找到softlink1這個連結檔案的inode.
軟連線, 普通檔案和檔案夾一樣,都透過inode table來描述:
透過上面章節相同的方法dump 根目錄的inode指向的資料塊, 我們可以看到一個新的檔案softlink1已經被新增. 下圖中,對於softlink1這個檔案的描述是這樣的:
-
0x0000000d 意為這個檔案的inode index.
-
0x09意為檔案名字的長度
-
0x07意為檔案的型別是soft link.
接著, 我們來dump inode index 0x0d上的檔案內容:
0xa1ff中的0xa意為這個檔案是一個softlink型別的檔案(詳情請見inode資料格式定義), 對於這種型別的檔案, ext4的處理方式是這樣的:
The target of a symbolic link will be stored in this field if the target string is less than 60 bytes long. Otherwise, either extents or block maps will be used to allocate data blocks to store the link target.
意思就是, 如果標的檔案名的大小< 60B, 則存放在inode的i_block的資料結構中,否則就分配一個extents 去存放長度更長的標的檔案名.
刪除檔案後的檔案系統分析
檔案刪除操作,作為檔案建立的反向操作, 大致的原理是找到檔案的inode, 修改檔案的inode, 釋放inode(free inode number)和data block.
在將跟目錄下的檔案file1.txt刪除後, 我們分別dump一下被刪除檔案的inode內容, 被刪除檔案的檔案內容, 根目錄的extent內容.
刪除檔案的inode: extent的block number 和number of block變為 0. 刪除時間由原先的0變更為0x5976f7a3(epoch format), 也就是標記這個inode被delete了, 它沒有指向任何資料, 解除了和原先檔案file1.txt的關係(如上圖).
檔案內容則依然存在,只是這個原先的extent佔用的塊已經被釋放了:
根目錄的extent內容沒有發生變化:
也就是說,刪除檔案其實就是操作了對應檔案的inode table.
終了
本文使用了dd,hexdump, dumpe2fs, ls四個命令完成了對ext4檔案系統的簡單的分析實踐,之所以說簡單,是因為本文只包含了對檔案系統基本操作的分析實踐, 但是觸類旁通, 希望這篇文章能夠給大家帶來一定的幫助, 同時, 也希望大牛能夠給本文指出不足,小弟先謝謝大家了.
另外,筆者之所以沒有使用debugfs工具來分析ext4是為了讓大家更直觀的瞭解分析實踐的過程.