前言: 目前大部分Linux操作系統使用的文件系統是ext4和xfs, 了解ext4在磁盤中的分布
1. 容量概念
對於儲存幾個概念的解析:
sector(扇區) :
1.磁盤最小的儲存單位,可以通過命令行 fdisk -l得知單位每sector的大小(一般是512byte)
2.機械硬盤HDD的可用空間大小計算公式是 heads(磁頭數量) cylinders(柱面數量) sectors(扇區數量) * 每個sector大小(512byte)
3.所以固態可用空間的總大小是 sectors(扇區數量) * 每個sector大小(512byte)。
4.這幾個屬性是固定不能修改, 但可以通過命令讀取得到。因為固態硬盤SSD沒有磁頭柱面的概念。
// 1073741824 bytes的大小剛好是 sectors * 512 bytes 得出來的 root@xxxxxx:~# fdisk -l /dev/rbd0 Disk /dev/rbd0: 1 GiB, 1073741824 bytes, 2097152 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 4194304 bytes / 4194304 bytes Disklabel type: dos Disk identifier: 0x5510f42b Device Boot Start End Sectors Size Id Type /dev/rbd0p1 8192 2097151 2088960 1020M 83 Linux root@xxxxxx:~#
block (塊) :
1.是文件系統EXT4,FAT32,XFS等最小的儲存單位,使用命令 blkid 可查看文件系統類型。
2.每一個block只能存儲一個文件的數據。
3.當格式化一個文件系統時,如果選擇不當,就會造成大量的磁盤空間浪費。
4.默認操作系統每個block的大小是4k(4096bytes),一個block由連續的sector組成。
5.一個文件在文件系統中的block不一定是連續的。
inode (索引節點) :
1.記錄文件的權限、屬性和數據所在塊block的號碼。
2.每個文件都有且僅有一個的inode,每個inode都有自己的編號,可以把inode簡單地理解為文檔索引。
data block (數據區塊) :
1.block可以區分成兩種概念,第一就是上述說的文件系統中最小的儲存單位(存在1個block存下多個inode的說法),第二也是這里談及到數據區塊
2.存儲的文件內容,也叫數據區塊(data block),每個block都有自己的編號,Ext2支持的單位block容量僅為1k、2k、4k
目錄的data block保存該目錄下所有文件以及子目錄的名字,inode編號等信息。
2. Ext4文件系統磁盤布局
Ext4文件系統把整個分區划分成各個block group(塊組),每個block group由superblock(超級塊),block group(區塊群組), block bitmap(區塊對照表), inode bitmap(inode 對照表)和group descriptor以及inode table、data block組成。
1024 bytes 的 Group 0 Padding(boot block)只有 塊組0 有,用於裝載該分區的操作系統。MBR 為主引導記錄用來引導計算機。在計算機啟動時,BIOS 讀入並執行 MBR,MBR 作的第一件事就是確定活動分區(這對應於雙系統的計算機開機時選擇啟動項,單系統的直接就能確定了所以就不需要選擇),讀入活動分區的引導塊(Boot block),引導塊再加載該分區中的操作系統。
官方網站:https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
圖2為簡略圖省略了塊組描述與預留GDT部分。


superblock(超級塊)
1.記錄文件系統(filesystem)的整體信息,包括inode/block的總量、使用量、剩余量、大小、以及文件系統的格式和相關信息。
備注:superblock對於文件系統太重要了,但是文件系統的superblock又只有一個,所以除了第一個block group含有superblock外,后續block group都可能會含有備份的superblock,目的就是為了避免superblock單點無法救援的問題。
block bitmap(區塊對照表)
1.一個block只能被一個文件使用,當我們新增文件時,肯定需要使用新的block來記錄文件數據。那么如何快速地知道,哪些block是新的?哪些block是已經使用了的?block bitmap就是這樣被設計出來,記錄所有使用和未使用的block號碼。同樣的,
2.我們刪除文件時,先從block bitmap中找到對應的block號碼,然后更新標志為未使用,最后釋放block。
inode bitmap(inode 對照表)
和block bitmap一樣的設計理念,只不過它記錄地是已使用和未使用的inode號碼,這里就不再敖述了。
group descriptor
描述每個區段(block group)開始和結束的block號碼,以及說明每個區段(inodemap、blockmap、inode table)分別介於哪些block號碼之間。
inode table
該組所有inode的合集,可以通過inode編號 以inode table起始位置作偏移找到inode所在的位置。
data block
上節已敘述,為存放文件或目錄數據的地方。

2.1 獲取ext4分區相關信息
可以使用dumpe2fs 打印ext4分區的詳細信息。
Inode count: 65280 Block count: 261120 Block size: 4096 Inode size: 256 Blocks per group: 32768 Inodes per group: 8160
可以從以上獲取得整個分區inode的數量是65280 block的數量261120,一個block的大小是4K,每個block group有 32768 block (4096 * 32768 大小)
其中一個block 可以存放 4096Bytes (block size) / 2566Bytes (Inode size) = 16 個 inode
一個 block group 有 8160 個 inode
root@xxxxxx:~# dumpe2fs /dev/rbd0p1 dumpe2fs 1.44.1 (24-Mar-2018) Filesystem volume name: <none> Last mounted on: /mnt/ceph-vol1 Filesystem UUID: 6f074db1-8f6f-4313-851d-fa3c9b7590f8 Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum Filesystem flags: signed_directory_hash Default mount options: user_xattr acl Filesystem state: clean Errors behavior: Continue Filesystem OS type: Linux Inode count: 65280 Block count: 261120 Reserved block count: 13056 Free blocks: 247156 Free inodes: 65267 First block: 0 Block size: 4096 Fragment size: 4096 Group descriptor size: 64 Reserved GDT blocks: 127 Blocks per group: 32768 Fragments per group: 32768 Inodes per group: 8160 Inode blocks per group: 510 RAID stride: 1024 RAID stripe width: 1024 Flex block group size: 16 Filesystem created: Wed Oct 14 17:42:24 2020 Last mount time: Thu Oct 15 17:35:48 2020 Last write time: Thu Oct 15 17:35:48 2020 Mount count: 2 Maximum mount count: -1 Last checked: Wed Oct 14 17:42:24 2020 Check interval: 0 (<none>) Lifetime writes: 153 MB Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root) First inode: 11 Inode size: 256 Required extra isize: 32 Desired extra isize: 32 Journal inode: 8 Default directory hash: half_md4 Directory Hash Seed: 6a8799b4-6528-499d-9f62-fde45cb8991a Journal backup: inode blocks Checksum type: crc32c Checksum: 0x1ab86e25 Journal features: journal_64bit journal_checksum_v3 Journal size: 16M Journal length: 4096 Journal sequence: 0x00000010 Journal start: 1 Journal checksum type: crc32c Journal checksum: 0xdc3ea2cb Group 0: (Blocks 0-32767) csum 0xe2fe [ITABLE_ZEROED] Primary superblock at 0, Group descriptors at 1-1 Reserved GDT blocks at 2-128 Block bitmap at 129 (+129), csum 0x06292657 Inode bitmap at 137 (+137), csum 0xc6f39952 Inode table at 145-654 (+145) 28533 free blocks, 8143 free inodes, 6 directories, 8143 unused inodes Free blocks: 4235-32767 Free inodes: 18-8160 Group 1: (Blocks 32768-65535) csum 0x4d02 [INODE_UNINIT, ITABLE_ZEROED] Backup superblock at 32768, Group descriptors at 32769-32769 Reserved GDT blocks at 32770-32896 Block bitmap at 130 (bg #0 + 130), csum 0xfa6bcbac Inode bitmap at 138 (bg #0 + 138), csum 0x00000000 Inode table at 655-1164 (bg #0 + 655) 27518 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 32898-33791, 38912-65535 Free inodes: 8161-16320 Group 2: (Blocks 65536-98303) csum 0xba1c [INODE_UNINIT, ITABLE_ZEROED] Block bitmap at 131 (bg #0 + 131), csum 0x8c3dbe87 Inode bitmap at 139 (bg #0 + 139), csum 0x00000000 Inode table at 1165-1674 (bg #0 + 1165) 28672 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 69632-98303 Free inodes: 16321-24480 Group 3: (Blocks 98304-131071) csum 0x579e [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED] Backup superblock at 98304, Group descriptors at 98305-98305 Reserved GDT blocks at 98306-98432 Block bitmap at 132 (bg #0 + 132), csum 0x00000000 Inode bitmap at 140 (bg #0 + 140), csum 0x00000000 Inode table at 1675-2184 (bg #0 + 1675) 32639 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 98433-131071 Free inodes: 24481-32640 Group 4: (Blocks 131072-163839) csum 0xfc69 [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED] Block bitmap at 133 (bg #0 + 133), csum 0x00000000 Inode bitmap at 141 (bg #0 + 141), csum 0x00000000 Inode table at 2185-2694 (bg #0 + 2185) 32768 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 131072-163839 Free inodes: 32641-40800 Group 5: (Blocks 163840-196607) csum 0x0b15 [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED] Backup superblock at 163840, Group descriptors at 163841-163841 Reserved GDT blocks at 163842-163968 Block bitmap at 134 (bg #0 + 134), csum 0x00000000 Inode bitmap at 142 (bg #0 + 142), csum 0x00000000 Inode table at 2695-3204 (bg #0 + 2695) 32639 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 163969-196607 Free inodes: 40801-48960 Group 6: (Blocks 196608-229375) csum 0x18c8 [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED] Block bitmap at 135 (bg #0 + 135), csum 0x00000000 Inode bitmap at 143 (bg #0 + 143), csum 0x00000000 Inode table at 3205-3714 (bg #0 + 3205) 32768 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 196608-229375 Free inodes: 48961-57120 Group 7: (Blocks 229376-261119) csum 0x9bc0 [INODE_UNINIT, ITABLE_ZEROED] Backup superblock at 229376, Group descriptors at 229377-229377 Reserved GDT blocks at 229378-229504 Block bitmap at 136 (bg #0 + 136), csum 0xde491fa2 Inode bitmap at 144 (bg #0 + 144), csum 0x00000000 Inode table at 3715-4224 (bg #0 + 3715) 31615 free blocks, 8160 free inodes, 0 directories, 8160 unused inodes Free blocks: 229505-261119 Free inodes: 57121-65280
2.2 Ext4塊數據屬性
要定位文件在磁盤的位置,首先要清楚ionde 和data block的數據內容。
在 Ext4 中除日志以外的數據都是以小端法存儲的。
不過多對查找文件內容不起作用的部分進行敘述。
2.2.1 superblock 超級塊
superblock記錄有關封閉文件系統的各種信息,例如塊計數、inode計數、支持的功能、維護信息等等。
如果設置了sparse_super feature標志,則超級塊和組描述符的冗余副本僅保留在組號為0或3、5或7的冪次方的組中。如果未設置該標志,則所有組中都會保留冗余副本。
超級塊校驗和是根據包含FS UUID的超級塊結構計算的。
ext4超級塊在struct ext4_super_block中的布局如下:


2.2.2 Block Group Descriptors 塊組描述
詳細查詢官網。
在一個塊組中,擁有固定位置的數據結構只有超級塊和塊組描述符。
flex_bg 和 meta_bg 並不是互斥關系。
ext4 64-bits 特性未開啟時占用 32 bytes,開啟時占用 64 bytes。
2.2.3 Inode Table inode表
inode存儲與文件相關的所有元數據(時間戳、塊映射、擴展屬性等)
inode表是struct ext4_inode的線性數組。該表的大小足以存儲至少sb.s_inode_size * sb.s_inodes_per_group字節。包含inode的塊組的編號可以計算為(inode_number-1)/sb.s_inodes_per_group,並且組表中的偏移量為(inode_number-1)%sb.s_inodes_per_group。沒有inode 0。



![]()
2.2.4 Extent Tree 文件的儲存方式
每個extent節點由一個頭部和多個body組成,無論是索引節點還是葉子節點,甚至是直接存儲在inode中的extent節點。當一個文件占用的extent數量較少的時候,其extent可直接存儲在inode.i_block[ ]中,當extent數量超過inode.i_block[ ]的存儲容量的時候( 由上圖inode結構表可知,inode.i_block最大值是60bytes,去除了extent_header 12bytes之后剩下48bytes只夠存4個extent),ext4便會用B樹來組織所有的extent。B樹上的每個節點,無論是索引節點還是葉子節點,其主體包含兩個部分:extent_header和extent_body。每個節點包含一個extent_header和多個extent_body。
B樹上的節點能存在 block size(4096bytes) - extent_header(12bytes) / extent_body(12bytes) = 340個extent 遠比indoe上的4個大!
ext4_extent_idx 就是索引節點,他指向的是下個節點或者葉子,葉子ext4_extent 才具有指向文件內容的data block的信息。


The extent tree header is recorded in struct ext4_extent_header, which is 12 bytes long:

Internal nodes of the extent tree, also known as index nodes, are recorded as struct ext4_extent_idx, and are 12 bytes long:

Leaf nodes of the extent tree are recorded as struct ext4_extent, and are also 12 bytes long:

2.2.5 ext4_dir_entry/ext4_dir_entry2 目錄存放
目錄也是文件, 也有 inode, inode 指向一個塊, 塊中保存各個文件信息, ext4_dir_entry 包括文件名和 inode, 默認按列表存
ext4_dir_entry

ext4_dir_entry_2

2.2.6 Hash Tree 目錄儲存方式
線性目錄項不利於系統性能提升。因而從ext3開始加入了快速平衡樹哈希目錄項名稱。如果在inode中設置EXT4_INDEX_FL標志,目錄使用哈希的B樹(hashed btree ,htree)組織和查找目錄項。為了向后只讀兼容Ext2,htree實際上隱藏在目錄文件中。
Ext2的慣例,樹的根總是在目錄文件的第一個數據塊中。“.”和“..”目錄項必須出現在第一個數據塊的開頭。因而這兩個目錄項在數據塊的開頭存放兩個struct ext4_dir_entry_2結構,且它們不存到樹中。根結點的其他部分包含樹的元數據,最后一個hash->block map查找到htree中更低的節點。如果dx_root.info.indirect_levels不為0,那么htree有兩層;htree根結點的map指向的數據塊是一個內部節點,由一個minor hash索引。Htree中的內部節點的minor_hash->block map之后包含一個零化的(zeroed out) structext4_dir_entry_2找到葉子節點。葉子節點包括一個線性的struct ext4_dir_entry_2數組;所有這些項都哈希到相同的值。如果發生溢出,目錄項簡單地溢出到下一個葉子節點,哈希的least-significant位(內部節點的map)做相應設置。以htree的方式遍歷目錄,計算要查找的目錄文件名稱的哈希值,然后使用哈希值找到對應的數據塊號。如果樹是flat,該數據塊是目錄項的線性數組,因而可被搜索到;否則,計算文件名稱的minor hash,並使用minor hash查找相應的第三個數據塊號。第三個數據塊是目錄項線性數組。
Htree的根 :struct dx_root
Htree的內部節點: struct dx_node
Htree 樹根和節點中都存在的 Hashmap: struct dx_entry


The root of the htree is in struct dx_root, which is the full length of a data block:

Interior nodes of an htree are recorded as struct dx_node, which is also the full length of a data block:

If metadata checksums are enabled, the last 8 bytes of the directory block (precisely the length of one dx_entry) are used to store a struct dx_tail, which contains the checksum. The limit and count entries in the dx_root/dx_node structures are adjusted as necessary to fit the dx_tail into the block. If there is no space for the dx_tail, the user is notified to run e2fsck -D to rebuild the directory index (which will ensure that there’s space for the checksum. The dx_tail structure is 8 bytes long and looks like this:


2. 通過dd了解文件在磁盤中的內容
把ext4分區掛載到ceph-vol1目錄上,可以直接通過查看分區每個block的數據來了解文件系統中的各個概念
root@xxxxxx:/mnt/ceph-vol1# tree ./ ./ ├── AAA │ ├── 123 │ ├── 13344 │ ├── MN212142_9392.csv │ ├── random_data │ └── tiantian ├── lost+found ├── random_data └── test.txt root@xxxxxx:/mnt/ceph-vol1# stat ./ File: ./ Size: 4096 Blocks: 8 IO Block: 4096 directory Device: fb01h/64257d Inode: 2 Links: 4 Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2020-10-22 09:21:12.342641001 +0800 Modify: 2020-10-22 09:21:11.526615694 +0800 Change: 2020-10-22 09:21:11.526615694 +0800 Birth: - root@xxxxxx:/mnt/ceph-vol1# stat test.txt File: AAA/test.txt Size: 52 Blocks: 8 IO Block: 4096 regular file Device: fb01h/64257d Inode: 12 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2020-10-15 17:36:05.266403535 +0800 Change: 2020-10-22 09:21:11.526615694 +0800 Birth: - root@xxxxxx:/mnt/ceph-vol1# dumpe2fs /dev/rbd0p1 ***** Group 0: (Blocks 0-32767) csum 0xe2fe [ITABLE_ZEROED] Primary superblock at 0, Group descriptors at 1-1 Reserved GDT blocks at 2-128 Block bitmap at 129 (+129), csum 0x06292657 Inode bitmap at 137 (+137), csum 0xc6f39952 Inode table at 145-654 (+145) 28533 free blocks, 8143 free inodes, 6 directories, 8143 unused inodes Free blocks: 4235-32767 Free inodes: 18-8160 ***
得知 ceph-vol1的innode編號是2 目錄下有個AAA目錄,當中text.txt的inode編號是12。
通過以下線路獲取text.txt的內容
從 Group Descriptors —> inode table —-> ceph-vol1 inode —> ceph-vol1 extent Data block 獲得 ceph-vol1 的信息
從 ceph-vol1 extent Data block —> test.txt inode—> test.txt Data block 獲得text.txt內容
第一步讀取超級塊信息,獲得根目錄ceph-vol1信息。
從文檔磁盤以及ext4概念 得知,塊組第0組必定有超級塊的信息,使用DD命令讀取對應的偏移量。
讀取超級塊數據

塊的大小是4096bytes,skip是0 , 偏移量是0x400(1024的Group 0 Padding) 使用DD命令讀取
dd if=/dev/rbd0p1 bs=4096 skip=0 | hexdump -C -n 3000 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000400 00 ff 00 00 00 fc 03 00 00 33 00 00 74 c5 03 00 |.........3..t...| //superblock起始 00000410 f3 fe 00 00 00 00 00 00 02 00 00 00 02 00 00 00 |................| 00000420 00 80 00 00 00 80 00 00 e0 1f 00 00 f4 17 88 5f |..............._| 00000430 f4 17 88 5f 02 00 ff ff 53 ef 01 00 01 00 00 00 |..._....S.......| 00000440 00 c8 86 5f 00 00 00 00 00 00 00 00 01 00 00 00 |..._............| 00000450 00 00 00 00 0b 00 00 00 00 01 00 00 3c 00 00 00 |............<...| 00000460 c6 02 00 00 6b 04 00 00 6f 07 4d b1 8f 6f 43 13 |....k...o.M..oC.| 00000470 85 1d fa 3c 9b 75 90 f8 00 00 00 00 00 00 00 00 |...<.u..........| 00000480 00 00 00 00 00 00 00 00 2f 6d 6e 74 2f 63 65 70 |......../mnt/cep| 00000490 68 2d 76 6f 6c 31 00 00 00 00 00 00 00 00 00 00 |h-vol1..........| 000004a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| *
代入supperblock的結構體得到
Total inode count = 65280
Total block count = 261120
Blocks per group = 32768
Inodes per group = 8160
與 dumpe2fs /dev/rbd0p1 得出結果一致,證明直接讀取磁盤數據的偏移量計算是正確的。

讀取Group Descriptors塊組描述符表
要獲取第0組塊的塊描述,可以增大偏移量跳過超級塊的位置,把skip改成1 獲得塊描述的信息
注意:所有塊組的描述塊都是放在一起。 起始第一個block剛好對應第0組塊。
root@xxxxxx:~# dd if=/dev/rbd0p1 bs=4096 skip=1 | hexdump -C -n 3000 00000000 81 00 00 00 89 00 00 00 91 00 00 00 75 6f cf 1f |............uo..| //看這一行 00000010 06 00 04 00 00 00 00 00 57 26 52 99 cf 1f fe e2 |........W&R.....| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 29 06 f3 c6 00 00 00 00 |........).......| 00000040 82 00 00 00 8a 00 00 00 8f 02 00 00 7e 6b e0 1f |............~k..| 00000050 00 00 05 00 00 00 00 00 ac cb 00 00 e0 1f 02 4d |...............M| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000070 00 00 00 00 00 00 00 00 6b fa 00 00 00 00 00 00 |........k.......| 00000080 83 00 00 00 8b 00 00 00 8d 04 00 00 00 70 e0 1f |.............p..| 00000090 00 00 05 00 00 00 00 00 87 be 00 00 e0 1f 1c ba |................| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000b0 00 00 00 00 00 00 00 00 3d 8c 00 00 00 00 00 00 |........=.......| 000000c0 84 00 00 00 8c 00 00 00 8b 06 00 00 7f 7f e0 1f |................| 000000d0 00 00 07 00 00 00 00 00 00 00 00 00 e0 1f 9e 57 |...............W| 000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

這樣就獲取得到組0的inode table的起始位置145 與dumpe2fs /dev/rbd0p1一致。
讀取inode table 與 inode信息
root@xxxxxx:~# dd if=/dev/rbd0p1 bs=4096 skip=145 | hexdump -C -n 2000 00000000 00 00 00 00 00 00 00 00 01 c8 86 5f 01 c8 86 5f |..........._..._| 00000010 01 c8 86 5f 00 00 00 00 00 00 00 00 00 00 00 00 |..._............| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000070 00 00 00 00 00 00 00 00 00 00 00 00 9e c7 00 00 |................| 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000100 ed 41 00 00 00 10 00 00 88 de 90 5f 87 de 90 5f |.A........._..._| //此處目錄 00000110 87 de 90 5f 00 00 00 00 00 00 04 00 08 00 00 00 |..._............| 00000120 00 00 08 00 0c 00 00 00 0a f3 01 00 04 00 00 00 |................| 00000130 00 00 00 00 00 00 00 00 01 00 00 00 81 10 00 00 |................| 00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000170 00 00 00 00 00 00 00 00 00 00 00 00 7f 57 00 00 |.............W..| 00000180 20 00 1f 92 38 12 8e 7d 38 12 8e 7d a4 25 b1 51 | ...8..}8..}.%.Q| 00000190 01 c8 86 5f 00 00 00 00 00 00 00 00 00 00 00 00 |..._............| 000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
可以通過公式推得出,這個
block group = (inode_number - 1) / sb.s_inodes_per_group
inode table index = (inode_number - 1) % sb.s_inodes_per_group
byte address in the inode table = (inode table index) * sb->s_inode_size
得出 00000100 的位置是 ceph-vol1 indoe數據 , 00000128 則是i_block[EXT4_N_BLOCKS=15] extent tree的起始位置。

ceph-vol1 的inode 只有一個 extent 並且是葉子節點指向data block,目錄inode的data block是Directory Entries結構體 struct dx_root,
i_block開頭再偏移12bytes 得出extent 指向的data block編號 0x1081 (4225)
讀取 目錄extent數據
root@xxxxxx:~# dd if=/dev/rbd0p1 bs=4096 skip=4225 | hexdump -C -n 2000 00000000 02 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00 |................| 00000010 0c 00 02 02 2e 2e 00 00 0b 00 00 00 24 00 0a 02 |............$...| 00000020 6c 6f 73 74 2b 66 6f 75 6e 64 00 00 0c 00 00 00 |lost+found......| 00000030 10 00 08 01 74 65 73 74 2e 74 78 74 0d 00 00 00 |....test.txt....| 00000040 14 00 0b 01 72 61 6e 64 6f 6d 5f 64 61 74 61 77 |....random_dataw| 00000050 0e 00 00 00 a4 0f 03 02 41 41 41 01 2e 74 65 73 |........AAA..tes| 00000060 74 2e 74 78 74 2e 73 77 78 00 00 00 00 00 00 00 |t.txt.swx.......| 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000007d0
extent 數據中包含了當前目錄,上一級目錄,當前子目錄和當前目錄文件的信息
當前目錄
ext4_dir_entry_2

lost+found
ext4_dir_entry_2

test.txt
ext4_dir_entry_2

可以得出 test.txt的inode編號 與 stat 結果是一致 為 12
讀取文件indoe 以及 extent
再次回到上面inode table
把 12代入 inode_number
block group = (inode_number - 1) / sb.s_inodes_per_group
inode table index = (inode_number - 1) % sb.s_inodes_per_group
byte address in the inode table = (inode table index) * sb->s_inode_size
得
block group = 0
inode table index = 11
byte address in the inode table = 11 * 256 = 0xB00
root@xxxxxx:~# dd if=/dev/rbd0p1 bs=4096 skip=145 | hexdump -C -n 30000 * 00000b00 a4 81 00 00 34 00 00 00 05 18 88 5f 87 de 90 5f |....4......_..._| //此處 00000b10 01 18 88 5f 00 00 00 00 00 00 01 00 08 00 00 00 |..._............| 00000b20 00 00 08 00 01 00 00 00 0a f3 01 00 04 00 00 00 |................| 00000b30 00 00 00 00 00 00 00 00 01 00 00 00 81 80 00 00 |................| 00000b40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| *
找到0xf30a位置再確認extent,得出data block 編號是 0x8081 (32897) ,便可知道text.txt的內容
dd if=/dev/rbd0p1 bs=4096 skip=32897 | hexdump -C -n 1000 00000000 68 65 6c 6c 6f 20 77 6f 72 6c 64 6c 73 6c 73 6c |hello worldlslsl| 00000010 73 6c 73 20 2d 2d 2d 2d 2d 2d 2d 2d 20 66 75 63 |sls -------- fuc| 00000020 6b 20 66 75 66 75 66 66 66 66 66 66 66 0a 66 64 |k fufufffffff.fd| 00000030 64 66 64 0a 00 00 00 00 00 00 00 00 00 00 00 00 |dfd.............| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000003e8 root@xxxxxx:/mnt/ceph-vol1/AAA# cat test.txt hello worldlslslsls -------- fuck fufufffffff fddfd
使用debugfs工具直接找出內容
使用debugfs 可以直接獲得 文件/目錄 data block 指向ext4_extent/ext4_dir_entry2 的編號
root@xxxxxx:/mnt/ceph-vol1/AAA# debugfs /dev/rbd0p1 debugfs 1.44.1 (24-Mar-2018) debugfs:stat ./ Inode: 14 Type: directory Mode: 0755 Flags: 0x80000 Generation: 2546967618 Version: 0x00000000:00000007 User: 0 Group: 0 Project: 0 Size: 4096 File ACL: 0 Links: 5 Blockcount: 8 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x5f91560e:e4814ef8 -- Thu Oct 22 17:51:10 2020 atime: 0x5f915612:a96762a8 -- Thu Oct 22 17:51:14 2020 mtime: 0x5f91560e:e4814ef8 -- Thu Oct 22 17:51:10 2020 crtime: 0x5f8916fe:3c52facc -- Fri Oct 16 11:43:58 2020 Size of extra inode fields: 32 Inode checksum: 0x704ebab8 EXTENTS: (0):4231 debugfs:stat ./test.txt Inode: 12 Type: regular Mode: 0644 Flags: 0x80000 Generation: 3671053201 Version: 0x00000000:00000001 User: 0 Group: 0 Project: 0 Size: 52 File ACL: 0 Links: 1 Blockcount: 8 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x5f90de87:7d8e1238 -- Thu Oct 22 09:21:11 2020 atime: 0x5f915207:02d0eb1c -- Thu Oct 22 17:33:59 2020 mtime: 0x5f881801:d53ae608 -- Thu Oct 15 17:36:01 2020 crtime: 0x5f86ca79:4762b4f8 -- Wed Oct 14 17:52:57 2020 Size of extra inode fields: 32 Inode checksum: 0xa5bced70 EXTENTS: (0):32897
3. ceph object分布 與 ext4 塊對應關系
ext4分區最小單位是一個block默認是4KB,在磁盤上是8個連續的sector。 ceph的oject大小默認是4M 等於可以保存 1024個 block,8192個 sector。

Disk /dev/rbd0: 1 GiB, 1073741824 bytes, 2097152 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 4194304 bytes / 4194304 bytes Disklabel type: dos Disk identifier: 0x5510f42b Device Boot Start End Sectors Size Id Type /dev/rbd0p1 8192 2097151 2088960 1020M 83 Linux
以上述的text.txt驗證
起始Sector(start) = 分區start + block number 8 = 8192 + 32897 8 = 271368
結束Sector(end) = 分區start + (block number + 1) 8 - 1 = 8192 + 32898 8 - 1 = 271375
root@xxxxxx:~# dd if=/dev/rbd0 bs=512 skip=271368 | hexdump -C -n 1000 00000000 68 65 6c 6c 6f 20 77 6f 72 6c 64 6c 73 6c 73 6c |hello worldlslsl| 00000010 73 6c 73 20 2d 2d 2d 2d 2d 2d 2d 2d 20 66 75 63 |sls -------- fuc| 00000020 6b 20 66 75 66 75 66 66 66 66 66 66 66 0a 66 64 |k fufufffffff.fd| 00000030 64 66 64 0a 00 00 00 00 00 00 00 00 00 00 00 00 |dfd.............| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000003e8
sector 在 ceph object中是連續
起始Object(start) = 起始Sector(start) / 8 = 33921
結束Object(end) = 結束Sector(end) / 8 = 33921
偏移量 = 起始Sector(start) % 8192
常用命令參考
fdisk -l dumpe2fs tree stat dd debugfs
結束語
ext4 文件系統並不復雜,分析當中的儲存架構可以使我們對操作系統有更好的了解。
