1. 程式人生 > >ext2文件系統

ext2文件系統

sys 數據恢復軟件 獲取 mon system v sparse acc clas con

很久以來,就想寫一篇關於ext 家族文件系統的文章,源於我剛工作的時候,曾經一不小心rm -rf,誤刪除了很多文件,當時真想有個數據恢復軟件能幫我把數據回復了。當然學習數據恢復,首先要學習文件系統。最近工作原因,好長時間沒看學習Linux kernel 相關的東西,感覺面目可憎。扯遠了,開始我們的ext2 文件系統的探索之旅。

那些介紹ext2特征的套話我就不說了,任何一本靠譜的linux教程中都可以找到,我們直接單刀直入,開始探索。 首先生成一個ext2文件系統。我在我磁盤空間有限的Ubuntu中,劃出500M的空間來從頭學習ext2 文件系統。 dd命令用來創建一個文件,不多說了,通過執行這個dd命令生成了一個全零的大小為512000*1KB的文件,即500MB 的文件。 losetup是設定循環設備(loop service)的,循環設備可以將文件模擬成塊設備。然後在塊設備上建立我們的ext2文件系統,來進行我們的學習。所以下面用mke2fs命令將loop設備格式化成ext2文件系統。 Oh,yeah,我們終於有了ext2文件系統。 這裏需要強調下,我們調用了mke2fs的默認選項其中:
  1. root@libin:~# dd if=/dev/zero of=bean bs=1K count=512000
  2. 記錄了512000 0 的讀入
  3. 記錄了512000 0 的寫出
  4. 524288000字節(524 MB)已復制,9.40989 秒,55.7 MB/秒
  5. root@libin:~# ll bean
  6. -rw-r--r-- 1 root root 524288000 2012-07-06 22:24 bean
  7. root@libin:~# ll -h bean
  8. -rw-r--r-- 1 root root 500M 2012-07-06 22:24 bean
  9. root@libin:~#
  10. root@libin:~#
  11. root@libin:~# losetup /dev/loop0 bean
  12. root@libin:~# cat /proc/partitions
  13. major minor #blocks name
  14. 7 0 512000 loop0
  15. 8 0 312571224 sda
  16. 8 1 49182966 sda1
  17. .......
  18. oot@libin:~# mke2fs /dev/loop0
  19. mke2fs 1.41.11 (14-Mar-2010)
  20. 文件系統標簽=
  21. 操作系統:Linux
  22. 塊大小=1024 (log=0)
  23. 分塊大小=1024 (log=0)
  24. Stride=0 blocks, Stripe width=0 blocks
  25. 128016 inodes, 512000 blocks
  26. 25600 blocks (5.00%) reserved for the super user
  27. 第一個數據塊=1
  28. Maximum filesystem blocks=67633152
  29. 63 block groups
  30. 8192 blocks per group, 8192 fragments per group
  31. 2032 inodes per group
  32. Superblock backups stored on blocks:
  33. 8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409
  34. 正在寫入inode表: 完成
  35. Writing superblocks and filesystem accounting information: 完成
  36. This filesystem will be automatically checked every 24 mounts or
  37. 180 days, whichever comes first. Use tune2fs -c or -i to override.
但是這樣還沒完,我們還是不能訪問我們新建的ext2文件系統,因為還沒有掛載,我決定將loop 設備掛載在/mnt/bean 目錄下。

  1. mkdir /mnt/bean
  2. mount -t ext2 /dev/loop0 /mnt/bean
  3. root@libin:/mnt/bean# mount
  4. .........
  5. /dev/loop0 on /mnt/bean type ext2 (rw)
  6. root@libin:/mnt/bean# ll
  7. 總用量 17
  8. drwxr-xr-x 3 root root 1024 2012-07-06 22:31 ./
  9. drwxr-xr-x 4 root root 4096 2012-07-06 22:32 ../
  10. drwx------ 2 root root 12288 2012-07-06 22:31 lost found/

經過我們的努力,我們終於創建好了我們的ext2文件系統。下面需要講講ext2文件系統的結構是什麽樣的了。


下面這張圖是經典的ext2文件系統的結構圖。網上到處可以找到這種類似的圖片,但是我非要畫這個圖片的原因是為了澄清2個問題: 1 並不是所有的塊組都有超級塊和快組描述符。 2 塊組描述符GDT並不是只管理自己這個塊組的信息,相反,它管理的是所有的塊組的信息。 技術分享圖片 (inode表和數據塊的個數不一定相等,我這個圖畫多少有點問題) 我們知道,超級塊是很重要的,因為它告訴了linux 這個塊設備是怎樣組織的,它告訴linux我這個文件系統是什麽文件系統,每個塊的大小是多大(1024、2048 or 4096),每個塊組有多少個塊,inode占多少個字節。等等的信息。正是因為超級塊很重要,所以我們不能將這些信息只保存1份。試想一下,如果超級塊壞掉了,而我們只有一個塊組有超級塊,那麽就徹底完蛋了,後面接近500M的空間及裏面的數據我們都沒辦法獲得了。這是比較容易理解的。但是,是不是每個塊組都要有啟動塊呢。這就沒必要了,這也有點空間浪費。那到底把超級塊放到那些塊組呢?
  1. Superblock backups stored on blocks:
  2. 8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409
這是格式化loop設備輸出到終端的result信息,因為每個塊組是8192個塊(原因後面講),所以第0個塊組 ,第1塊組,第3個塊組 第5個塊組,第7個塊組,第9個塊組,第25個塊組,第27個塊組,第49個塊組存儲有超級塊。 怎麽計算出來的,為什麽非要存在這些塊組?計算規則是3 5 和7的冪,這樣的塊組保存超級塊。 解釋塊組描述符之前我們先看下超級塊的相關信息:
  1. struct ext2_super_block {
  2. __u32 s_inodes_count;
  3. __u32 s_blocks_count;
  4. __u32 s_r_blocks_count;
  5. __u32 s_free_blocks_count;
  6. __u32 s_free_inodes_count;
  7. __u32 s_first_data_block;
  8. __u32 s_log_block_size;
  9. __u32 s_dummy3[7];
  10. unsigned char s_magic[2];
  11. __u16 s_state;
  12. ...
  13. }
下面我們通過debugfs來獲取一下ext2的相關信息。
  1. root@libin:/mnt/bean# dumpe2fs /dev/loop0
  2. dumpe2fs 1.41.11 (14-Mar-2010)
  3. Filesystem volume name: <none>
  4. Last mounted on: <not available>
  5. Filesystem UUID: 3bff7535-6f39-4720-9b64-1dc8cf9fe61d
  6. Filesystem magic number: 0xEF53
  7. Filesystem revision #: 1 (dynamic)
  8. Filesystem features: ext_attr resize_inode dir_index filetype sparse_super
  9. Filesystem flags: signed_directory_hash
  10. Default mount options: (none)
  11. Filesystem state: not clean
  12. Errors behavior: Continue
  13. Filesystem OS type: Linux
  14. Inode count: 128016
  15. Block count: 512000
  16. Reserved block count: 25600
  17. Free blocks: 493526
  18. Free inodes: 128005
  19. First block: 1
  20. Block size: 1024
  21. Fragment size: 1024
  22. Reserved GDT blocks: 256
  23. Blocks per group: 8192
  24. Fragments per group: 8192
  25. Inodes per group: 2032
  26. Inode blocks per group: 254
  27. Filesystem created: Fri Jul 6 22:31:09 2012
  28. Last mount time: Fri Jul 6 22:33:28 2012
  29. Last write time: Fri Jul 6 22:33:28 2012
  30. Mount count: 1
  31. Maximum mount count: 24
  32. Last checked: Fri Jul 6 22:31:09 2012
  33. Check interval: 15552000 (6 months)
  34. Next check after: Wed Jan 2 22:31:09 2013
  35. Reserved blocks uid: 0 (user root)
  36. Reserved blocks gid: 0 (group root)
  37. First inode: 11
  38. Inode size: 128
  39. Default directory hash: half_md4
  40. Directory Hash Seed: 0140915d-91ae-43df-9d84-9536cedc0d2b
  41. Group 0: (Blocks 1-8192)
  42. 主 superblock at 1, Group descriptors at 2-3
  43. 保留的GDT塊位於 4-259
  44. Block bitmap at 260 ( 259), Inode bitmap at 261 ( 260)
  45. Inode表位於 262-515 ( 261)
  46. 7663 free blocks, 2021 free inodes, 2 directories
  47. 可用塊數: 530-8192
  48. 可用inode數: 12-2032
  49. ...
  50. Group 62: (Blocks 507905-511999)
  51. Block bitmap at 507905 (+0), Inode bitmap at 507906 (+1)
  52. Inode表位於 507907-508160 (+2)
  53. 3839 free blocks, 2032 free inodes, 0 directories
  54. 可用塊數: 508161-511999
  55. 可用inode數: 125985-128016
OK ,我們拿到了這些信息,但是,我怎麽證明debugfs拿到的信息是對的呢。只有一個辦法,我們鉆到超級塊裏面,根據超級塊數據結構,獲得超級塊每個字段的值,聽起來很刺激吧,OK,Just DO IT。
  1. root@libin:/mnt/bean# dd if=/dev/loop0 bs=1k count=261 |od -tx1 -Ax > /tmp/dump_hex
  2. 記錄了261 0 的讀入
  3. 記錄了261 0 的寫出
  4. 267264字節(267 kB)已復制,0.0393023 秒,6.8 MB/秒
  5. root@libin:/mnt/bean# vi /tmp/dump_hex
我將整個loop設備前面的261K字節讀入了/tmp/dump_hex中。其中第0塊是啟動塊,按下不提。第一塊就是說super block。很激動,我們終於可以和傳說中的超級塊赤裸相見了。
  1. 000400 10 f4 01 00 00 d0 07 00 00 64 00 00 d6 87 07 00
  2. 000410 05 f4 01 00 01 00 00 00 00 00 00 00 00 00 00 00
  3. 000420 00 20 00 00 00 20 00 00 f0 07 00 00 5f cb f7 4f
  4. 000430 5f cb f7 4f 01 00 1a 00 53 ef 00 00 01 00 00 00
  5. 000440 25 cb f7 4f 00 4e ed 00 00 00 00 00 01 00 00 00
  6. 000450 00 00 00 00 0b 00 00 00 80 00 00 00 38 00 00 00
  7. 000460 02 00 00 00 01 00 00 00 5a 65 4b 92 fe 63 43 eb
  8. 000470 b6 86 3e f3 6e 44 19 af 00 00 00 00 00 00 00 00
  9. 000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  10. *
  11. 0004c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
  12. 0004d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  13. 0004e0 00 00 00 00 00 00 00 00 00 00 00 00 f9 6f 16 79
  14. 0004f0 b7 dc 4f 8a a1 a1 18 82 72 a7 d8 25 01 00 00 00
  15. 000500 00 00 00 00 00 00 00 00 25 cb f7 4f 00 00 00 00
  16. 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  17. *
  18. 000560 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  19. 000570 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  20. *
  21. 000800 04 01 00 00 05 01 00 00 06 01 00 00 ef 1d e5 07


最左邊一列是地址,16進制。000400=1K,換句話說,就是文件第1K個字節。000800 =2K,這就是我們朝思暮想的超級塊啊。我很激動,所以把整個超級塊都貼上了,幸好我不是靠字數來騙稿費的人,否則咱得被鄙視死。

再把ext2超級塊的數據結構貼上,咱挨個字段比較比較,看看debugfs說的對不?
  1. struct ext2_super_block {
  2. __u32 s_inodes_count;
  3. __u32 s_blocks_count;
  4. __u32 s_r_blocks_count;
  5. __u32 s_free_blocks_count;
  6. __u32 s_free_inodes_count;
  7. __u32 s_first_data_block;
  8. __u32 s_log_block_size;
  9. ...
  10. }
第一個字段叫s_inodes_count, 占四個字節。OK,我們看,從1K開始前四個字節是10 f4 01 00。我們知道有little-endian和big-endian。ext2設計者為了支持文件系統的可移動,規定磁盤上一律是little-endian,數據讀入內存中時,kernel來負責把格式轉成cpu的本機格式。 OK,是little-endian咱就明白了,不就是0x0001f410嘛 。 0x0001f410=128016,看看debugfs給我們的數據,Inode count: 128016,一模一樣。 再舉個例子,比如,我們關心free_blocks_count,查看數據結構,free_blocks_count字段起始位置是超級塊的第12字節。即00040c地址。看下的 d6 87 07 00。計算以下可以得到0x000787d6 = 493526,和debugfs 的Free blocks給出的一樣。OK。看管關心什麽字段,可以自己查看。通過和超級塊赤裸想見,我們知道了ext2 super block的結構。
最後總結一句,不是所有的塊組都有超級塊,超級塊只占1個block塊,沒錯,當blocksize為4K的時候,這個塊大多數空間是浪費的。不過還好,畢竟超級塊個數有限,浪費不了多少 下面講述 塊組描述符: 組描述符一共32個字節,大多數的教材都會給我們一組誤解,就是每個塊組,都要有組描述符。事實上並不是這樣。我們知道,一個組描述符只占32字節,而大多數的教材都會告訴我們,一個塊組裏面的組描述符占k個塊,一個組描述符是用不了這麽多空間的。 真相只有一個,就是所有的組描述符以數組的形式存放在k個塊中。也就是說,某個塊組可能沒有組描述符,而有組描述符的塊組,k個block中存放了所有組塊的組描述符。下面我來證實:
  1. struct ext2_group_desc
  2. {
  3. __u32 bg_block_bitmap; /* Blocks bitmap block */
  4. __u32 bg_inode_bitmap; /* Inodes bitmap block */
  5. __u32 bg_inode_table; /* Inodes table block */
  6. __u16 bg_free_blocks_count; /* Free blocks count */
  7. __u16 bg_free_inodes_count; /* Free inodes count */
  8. __u16 bg_used_dirs_count; /* Directories count */
  9. __u16 bg_flags;
  10. __u32 bg_exclude_bitmap_lo;/* Exclude bitmap for snapshots */
  11. __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap)LSB */
  12. __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap)LSB */
  13. __u16 bg_itable_unused; /* Unused inodes count */
  14. __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
  15. };

Group 0: (Blocks 1-8192) 主 superblock at 1, Group descriptors at 2-3 保留的GDT塊位於 4-259 Block bitmap at 260 (+259), Inode bitmap at 261 (+260) Inode表位於 262-515 (+261) 7663 free blocks, 2021 free inodes, 2 directories 可用塊數: 530-8192 可用inode數: 12-2032
  1. Group 1: (Blocks 8193-16384)
  2. 備份 superblock at 8193, Group descriptors at 8194-8195
  3. 保留的GDT塊位於 8196-8451
  4. Block bitmap at 8452 (+259), Inode bitmap at 8453 (+260)
  5. Inode表位於 8454-8707 (+261)
  6. 7677 free blocks, 2032 free inodes, 0 directories
  7. 可用塊數: 8708-16384
  8. 可用inode數: 2033-4064
  9. Group 2: (Blocks 16385-24576)
  10. Block bitmap at 16385 (+0), Inode bitmap at 16386 (+1)
  11. Inode表位於 16387-16640 (+2)
  12. 7936 free blocks, 2032 free inodes, 0 directories
  13. 可用塊數: 16641-24576
  14. 可用inode數: 4065-6096
看上圖,debugfs出來的信息,Group 2,並沒有所謂的組描述符。而Group1,用8194和8195兩個塊來存儲。OK,我們看下,裏面存儲的是什麽東西。 Group 0裏面第2和第3塊存儲的是組描述符,也就說從0x000800~0x001000是組描述符塊的內容。
  1. 000800 04 01 00 00 05 01 00 00 06 01 00 00 ef 1d e5 07
  2. 000810 02 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 塊組0的組描述符
  3. -----------------------------------------------------------------------
  4. 000820 04 21 00 00 05 21 00 00 06 21 00 00 fd 1d f0 07
  5. 000830 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 塊組1的組描述符
  6. -----------------------------------------------------------------------
  7. 000840 01 40 00 00 02 40 00 00 03 40 00 00 00 1f f0 07
  8. 000850 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 塊組2的組描述符
  9. ------------------------------------------------------------------------
  10. 000860 04 61 00 00 05 61 00 00 06 61 00 00 fd 1d f0 07
  11. 000870 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  12. 000880 01 80 00 00 02 80 00 00 03 80 00 00 00 1f f0 07
  13. 000890 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  14. 0008a0 04 a1 00 00 05 a1 00 00 06 a1 00 00 fd 1d f0 07
  15. 0008b0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  16. 0008c0 01 c0 00 00 02 c0 00 00 03 c0 00 00 00 1f f0 07
  17. 0008d0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  18. 0008e0 04 e1 00 00 05 e1 00 00 06 e1 00 00 fd 1d f0 07
  19. 0008f0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  20. 000900 01 00 01 00 02 00 01 00 03 00 01 00 00 1f f0 07
  21. 000910 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  22. 000fb0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  23. 000fc0 01 c0 07 00 02 c0 07 00 03 c0 07 00 ff 0e f0 07
  24. 000fd0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 塊組62的組描述符
  25. -----------------------------------------------------------------------
  26. 000fe0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  27. * 沒有塊組63
  28. ----------------------------------------------------------------------
  29. 001000 04 20 00 00 04 60 00 00 04 a0 00 00 04 e0 00 00
04 01 00 00 轉換成可讀的十進制是0x104=259,表示數據位圖位於第259塊block。inode位圖位於260,和debugfs出來的信息是一樣的(不算啟動塊)。0x1def=7663個空閑數據塊.... 各位看官可以自己解析任何一個塊組的相關信息,可以證明和debugfs出來的塊組的信息是一致的。現在我們確定了,組描述符以數組的形式存儲在K個快上,對於我們只有63個組塊,每個組塊需要32個字節,只需要2個1KB的block就足夠了。這就是說,其實組描述符和超級塊一樣,其實是冗余的。也就是說,其他存儲組描述符的兩個block,信息和塊組0中的組描述符的兩個block是一樣的。下面我來證明。 塊組25也有組描述符塊,204802和204803兩個塊,記錄了63個塊組的組描述符信息。內容應該和前面的塊組0的兩個塊一致。我已經取出了這兩個block的內容,大家自己比較吧,結果是內容是一樣的。

  1. Group 25: (Blocks 204801-212992)
  2. 備份 superblock at 204801, Group descriptors at 204802-204803
  3. 保留的GDT塊位於 204804-205059
  4. Block bitmap at 205060 (+259), Inode bitmap at 205061 (+260)
  5. Inode表位於 205062-205315 (+261)
  6. 7677 free blocks, 2032 free inodes, 0 directories
  7. 可用塊數: 205316-212992
  8. 可用inode數: 50801-52832

點擊(此處)折疊或打開

  1. root@libin:/mnt/bean# dd if=/dev/loop0 bs=1k skip=204802 count=2|od -tx1 -Ax > /tmp/dump_hex_
  2. 記錄了2+0 的讀入
  3. 記錄了2+0 的寫出
  4. 2048字節(2.0 kB)已復制,0.000160205 秒,12.8 MB/秒
  5. root@libin:/mnt/bean# vi /tmp/dump_hex_
  6. 000000 04 01 00 00 05 01 00 00 06 01 00 00 ef 1d e5 07
  7. 000010 02 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  8. 000020 04 21 00 00 05 21 00 00 06 21 00 00 fd 1d f0 07
  9. 000030 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  10. 000040 01 40 00 00 02 40 00 00 03 40 00 00 00 1f f0 07
  11. 000050 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  12. 000060 04 61 00 00 05 61 00 00 06 61 00 00 fd 1d f0 07
  13. 000070 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  14. 000080 01 80 00 00 02 80 00 00 03 80 00 00 00 1f f0 07
  15. 000090 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  16. 0000a0 04 a1 00 00 05 a1 00 00 06 a1 00 00 fd 1d f0 07
  17. 0000b0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  18. ....
  19. 0007c0 01 c0 07 00 02 c0 07 00 03 c0 07 00 ff 0e f0 07
  20. 0007d0 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
  21. 0007e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  22. *
  23. 000800
最後,最後的最後,解釋以下,為什麽每個塊組中的塊數blocks per group 是8192,因為,我們用1個塊作為位圖保存本塊組 block的使用情況(bit為1表示對應的block被使用,bit為0表示對應的block空閑),1個block是1024字節,共有1024*8=8192個bit,所以,每個塊組最多只能是81292個塊。 同樣道理如果用戶使用的是4094大小的塊,那麽,4096*8=32768個bit,所以每個塊組會有32K個塊。證據在下面。
  1. root@libin:/mnt/bean# cd /home
  2. root@libin:/home# umount /dev/loop0
  3. root@libin:/home# cd /mnt/bean
  4. root@libin:/mnt/bean# ll
  5. 總用量 8
  6. drwxr-xr-x 2 root root 4096 2012-07-06 22:32 ./
  7. drwxr-xr-x 4 root root 4096 2012-07-06 22:32 ../
  8. root@libin:/mnt/bean# mke2fs -b 4096 /dev/loop0
  9. mke2fs 1.41.11 (14-Mar-2010)
  10. 文件系統標簽=
  11. 操作系統:Linux
  12. 塊大小=4096 (log=2)
  13. 分塊大小=4096 (log=2)
  14. Stride=0 blocks, Stripe width=0 blocks
  15. 128000 inodes, 128000 blocks
  16. 6400 blocks (5.00%) reserved for the super user
  17. 第一個數據塊=0
  18. Maximum filesystem blocks=134217728
  19. 4 block groups
  20. 32768 blocks per group, 32768 fragments per group
  21. 32000 inodes per group
  22. Superblock backups stored on blocks:
  23. 32768, 98304
  24. 正在寫入inode表: 完成
  25. Writing superblocks and filesystem accounting information: 完成
  26. This filesystem will be automatically checked every 39 mounts or
  27. 180 days, whichever comes first. Use tune2fs -c or -i to override
參考文獻: 1 深入理解linux內核架構 2深入理解linux

ext2文件系統