EXT2
Intro(EXT2-FS) #
注意 #
Caution
a).
: 上述的图例是模拟 1M 的 ext2 文件系统,采用 block = 1024B ,inode size = 128B,进行划分的。b).
: 转换成数值的时候是小端读取。比如 [80 00 00 00] ==int=> 0x00000080 = 0x80 = 128。c).
: bitmap 采用的是 (MSB)10001001(LSB),参考 block-bitmap 中的部分描述、以及 wiki,比如 10000111,指的是第1,2,3,8个,不是1,6,7,8。准备 #
在 Ubuntu-22.04 上准备两个文件系统,一个干净的 fs1m,一个需要写入数据作对比 fs1m-with-data。 fs1m 备份下载、 fs1m-with-data 备份下载
创建一个1M的文件:dd if=/dev/zero of=fs1m count=256 bs=4K
格式化文件系统:mke2fs -b 1024 -I 128 fs1m
查看格式化后的信息:dumpe2fs fs1m
复制 fs1m 到 fs1m-with-data
挂载:mount -o loop fs1m-with-data /mnt
进入目录并写入数据:cd /mnt && mkdir 12302 && cd 12302 && echo 'something' > Hello.java
卸载:umount /mnt
使用xxd查看十六进制信息:xxd -a -u -g1 -s 1024 -l 16 fs1m
//使用*代替连续00,大写字母输出,一个字节分割,offset=1024, len=16。解析 #
普通属性的解析 #
# 读取 boot block 里面的内容,发现都是 00. ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 0' | bc` -l `echo '1024' | bc` fs1m 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000003f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 读取 super block 里面的前 264 个字节 # https://www.nongnu.org/ext2-doc/ext2.html#superblock-structure # s_inodes_count = (0x80) = 128(个) # s_blocks_count = (0x400) = 1024(个) ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 1' | bc` -l `echo '264' | bc` fs1m 00000400: 80 00 00 00 00 04 00 00 33 00 00 00 DA 03 00 00 ........3....... 00000410: 75 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 u............... 00000420: 00 20 00 00 00 20 00 00 80 00 00 00 00 00 00 00 . ... .......... 00000430: 4B 8D 0C 67 00 00 FF FF 53 EF 01 00 01 00 00 00 K..g....S....... 00000440: 4B 8D 0C 67 00 00 00 00 00 00 00 00 01 00 00 00 K..g............ 00000450: 00 00 00 00 0B 00 00 00 80 00 00 00 38 00 00 00 ............8... 00000460: 02 00 00 00 03 00 00 00 F4 24 E0 D7 33 DF 41 49 .........$..3.AI 00000470: 9B E9 F5 4B 47 00 86 29 00 00 00 00 00 00 00 00 ...KG..)........ 00000480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000004c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 ................ 000004d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000004e0: 00 00 00 00 00 00 00 00 00 00 00 00 77 9A 23 0D ............w.#. 000004f0: 64 91 41 16 93 8C 22 CF FB 46 9E 62 01 00 00 00 d.A..."..F.b.... 00000500: 0C 00 00 00 00 00 00 00 ........ # 读取 GDT 块组描述中的前 32 个字节 # bg_block_bitmap[4] = 06 00 00 00 = 6 # bg_inode_bitmap[4] = 07 00 00 00 = 7 # bg_inode_table[4] = 08 00 00 00 = 8 # bg_free_blocks_count[2] = DA 03 = 986 # bg_free_inodes_count[2] = 75 00 = 117 # bg_used_dirs_count[2] = 02 00 = 2 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 2' | bc` -l `echo '32' | bc` fs1m 00000800: 06 00 00 00 07 00 00 00 08 00 00 00 DA 03 75 00 ..............u. 00000810: 02 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 读取 GDT 保留块的中的的数据,发现都是 00 。上一个块是第 3 个,且里面的 block bitmap 是从 index=6 开始的,所以,3,4,5 都是 GDT 保留块 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 3' | bc` -l `echo '1024 * 3' | bc` fs1m 00000c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000017f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 读取 block bitmap 中的数据。通过 super block 中的属性 `s_blocks_count = 1024`个,所以虽然 block bitmap 占了一个块,但是只用了 1024 个bit,也就是 128 个字节,正好八行,我们可以打印十行看看。 # 发现除了前八行后,后面还会有 FF 之类的。完全不用关心这些。 # 第一行的 FF FF FF FF 1F 表示前 37 个 block 被占用了。(需要注意 1F(0b00011111)是从右往左算的) # 最后一行还有个 80 表示 (1000 0000),有 1 个block 被占用。 # 总共 1024 ,占用 37 + 1 = 38, 空闲 986,与 bg_free_blocks_count 相等。 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 6' | bc` -l `echo '16 * 10' | bc` fs1m 00001800: FF FF FF FF 1F 00 00 00 00 00 00 00 00 00 00 00 ................ 00001810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 00001870: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 ................ 00001880: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ 00001890: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ # 读取 inode bitmap 中的数据。通过 super block 中的属性 `s_inodes_count = 128`个,所以虽然 block bitmap 占了一个块,但是只用了 128 个bit,也就是 16 个字节,正好一行,我们可以打印两行看看。 # 与 block bitmap 类似,除了我们需要的第一行,其他的不用关心。 # 第一行的 FF 07 表示前 11 个 inode 被占用了。 前10个 inode 是被 ext2 文件系统保留的,其中第 2 个 inode 是根目录,第 11 个 inode 是lost+found 目录。 # 总共 128 ,占用 11, 空闲 117,与 bg_free_inodes_count 相等。 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 7' | bc` -l `echo '16 * 2' | bc` fs1m 00001c00: FF 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00001c10: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ # 读取 inode table 中的数据。因为 `s_inodes_count = 128`个 ,且 `s_inode_size = 128B`,一个block = 1024B ,所以一个 block 可以装 1024B / 128B = 8 个 inode。 128个就需要 128 / 8 = 16 块。 也就是 index[8,24)。 # 假设我们要读取第 2 个 inode,则如下打印: # 参考 https://www.nongnu.org/ext2-doc/ext2.html#inode-table # i_mode[2] = ED 41 = 0o40755 # i_uid[2] = 00 00 = 0 # i_size[4] = 00 04 00 00 = 1024 # i_atime[4] = 4B 8D 0C 67 = 1728875851 = 2024-10-14 11:17:31 # i_ctime[4] = 4B 8D 0C 67 # i_mtime[4] = 4B 8D 0C 67 # i_dtime[4] = 00 00 00 00 # i_gid[2] = 00 00 # i_links_count[2] = 03 00 # i_blocks[4] = 02 00 00 00 # i_flags[4] = 00 00 00 00 # i_osd1[4] = 00 00 00 00 # i_block[15 * 4]; (18 00 00 00 ....... 00 00),使用 15 个 32bit 整形值 来表示 inode 对应的数据块,前12个直接块,13一级,14二级,15三级 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 8 + 128' | bc` -l `echo '128' | bc` fs1m 00002080: ED 41 00 00 00 04 00 00 4B 8D 0C 67 4B 8D 0C 67 .A......K..gK..g 00002090: 4B 8D 0C 67 00 00 00 00 00 00 03 00 02 00 00 00 K..g............ 000020a0: 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 ................ 000020b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000020f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 去第 block[0] =(18 00 00 00)= 24 块中看看数据。 # 参考: https://www.nongnu.org/ext2-doc/ext2.html#linked-directory-entry-structure # inode[4] = 02 00 00 00 = 2 # rec_len[2] = 0C 00 = 12 # name_len[1] = 01 = 1 # file_type[1] = 02 = 2 # name[0-255] = 2E = 64 ==> ascii [.] # 所以此块有三个目录(.|..|lost+found) ,当前只分析了[.]目录,后面的两个同理。 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 24' | bc` -l `echo '1024' | bc` fs1m 00006000: 02 00 00 00 0C 00 01 02 2E 00 00 00 02 00 00 00 ................ 00006010: 0C 00 02 02 2E 2E 00 00 0B 00 00 00 E8 03 0A 02 ................ 00006020: 6C 6F 73 74 2B 66 6F 75 6E 64 00 00 00 00 00 00 lost+found...... 00006030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000063f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
带有数据的解析 #
# 目录结构: root@12302:~# tree /mnt /mnt ├── 12302 │ └── Hello.java └── lost+found # 从根目录出发 / = inode = 2 # block = block[0] = 18 00 00 00 = 24 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 8 + 128' | bc` -l `echo '128' | bc` fs1m-with-data 00002080: ED 41 00 00 00 04 00 00 1B B0 0C 67 1A B0 0C 67 .A.........g...g 00002090: 1A B0 0C 67 00 00 00 00 00 00 04 00 02 00 00 00 ...g............ 000020a0: 00 00 00 00 01 00 00 00 18 00 00 00 00 00 00 00 ................ 000020b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000020f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 查看 block = 24, 第 24 块里面的数据 # inode[4] = 0C 00 00 00 = 12 # rec_len[2] = D4 03 = 980 # name_len[1] = 05 = 5 # file_type[1] = 02 = 2 # name[0-255] = 31 32 33 30 32 ==> ascii [12302] ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 24' | bc` -l `echo '1024' | bc` fs1m-with-data 00006000: 02 00 00 00 0C 00 01 02 2E 00 00 00 02 00 00 00 ................ 00006010: 0C 00 02 02 2E 2E 00 00 0B 00 00 00 14 00 0A 02 ................ 00006020: 6C 6F 73 74 2B 66 6F 75 6E 64 00 00 0C 00 00 00 lost+found...... 00006030: D4 03 05 02 31 32 33 30 32 00 00 00 00 00 00 00 ....12302....... 00006040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000063f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 继续查看目录 12302 = inode = 12 # 12302 的 block = block[0] = 26 00 00 00 = 38 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 8 + 128 * 11' | bc` -l `echo '128' | bc` fs1m-with-data 00002580: ED 41 00 00 00 04 00 00 7C B0 0C 67 7A B0 0C 67 .A......|..gz..g 00002590: 7A B0 0C 67 00 00 00 00 00 00 02 00 02 00 00 00 z..g............ 000025a0: 00 00 00 00 0A 00 00 00 26 00 00 00 00 00 00 00 ........&....... 000025b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000025e0: 00 00 00 00 E3 A9 A0 96 00 00 00 00 00 00 00 00 ................ 000025f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 查看 block = 38, 第 38 块里面的数据 # 三个数据项[.|..|Hello.java] # inode[4] = 0E 00 00 00 = 14 # rec_len[2] = E8 03 = 1000 # name_len[1] = 0A = 10 # file_type[1] = 01 = 1 ,表示是一个文件 # name[0-255] = 48 65 6C 6C 6F 2E 6A 61 76 61 ==> ascii [Hello.java] ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 38' | bc` -l `echo '1024' | bc` fs1m-with-data 00009800: 0C 00 00 00 0C 00 01 02 2E 00 00 00 02 00 00 00 ................ 00009810: 0C 00 02 02 2E 2E 00 00 0E 00 00 00 E8 03 0A 01 ................ 00009820: 48 65 6C 6C 6F 2E 6A 61 76 61 00 00 00 00 00 00 Hello.java...... 00009830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 00009bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 继续查看文件 Hello.java = inode = 14 # Hello.java 的 block = block[0] = 2F 02 00 00 = 559 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 8 + 128 * 13' | bc` -l `echo '128' | bc` fs1m-with-data 00002680: A4 81 00 00 6F 00 00 00 68 B0 0C 67 7A B0 0C 67 ....o...h..gz..g 00002690: 68 B0 0C 67 00 00 00 00 00 00 01 00 02 00 00 00 h..g............ 000026a0: 00 00 00 00 01 00 00 00 2F 02 00 00 00 00 00 00 ......../....... 000026b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 000026e0: 00 00 00 00 98 DB 7D D7 00 00 00 00 00 00 00 00 ......}......... 000026f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 查看 block = 559, 第 559 块里面的数据 ➜ data git:(master) ✗ xxd -a -u -g1 -s `echo '1024 * 559' | bc` -l `echo '1024' | bc` fs1m-with-data 0008bc00: 70 75 62 6C 69 63 20 63 6C 61 73 73 20 48 65 6C public class Hel 0008bc10: 6C 6F 7B 0A 20 20 20 20 70 75 62 6C 69 63 20 73 lo{. public s 0008bc20: 74 61 74 69 63 20 76 6F 69 64 20 6D 61 69 6E 28 tatic void main( 0008bc30: 53 74 72 69 6E 67 5B 5D 20 61 72 67 73 29 7B 0A String[] args){. 0008bc40: 09 20 20 20 53 79 73 74 65 6D 2E 6F 75 74 2E 70 . System.out.p 0008bc50: 72 69 6E 74 6C 6E 28 22 48 65 6C 6C 6F 20 57 72 rintln("Hello Wr 0008bc60: 6F 6C 64 22 29 3B 0A 20 20 20 20 7D 0A 7D 0A 00 old");. }.}.. 0008bc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 0008bff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
示例结构图如下
多个块组 #
Note
有些文档有诸如此类的描述: 描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次mount的时间等等。超级块在每个块组的开头都有一份拷贝。
有一些疑问,比如什么样的拷贝,那些属性拷贝,还是完全拷贝等?
为了对描述中的拷贝精确定位,验证多个块组中的 super block 是否一致。需要生成一个多个 Block Group 的文件系统。
因为如果使用 block = 1024B 划分的话,每个块组最多可以包含块为 (block bitmap = 1024 * 8 = 8192) 个块。所以我们需要放大文件系统容量,这次采用 10M 的,还是按照 block = 1024B 进行划分,(10M / 1024B = 10240 块 > 8192)则会产生第二个 Block Group。
准备数据:dd if=/dev/zero of=fs10m count=10 bs=1024K
mke2fs -b 1024 -I 128 fs10m
可以使用 diff 命令快速比对
diff --color <(xxd -p -u -c 16 -s `echo '1024 * 1' | bc` -l `echo '264' | bc` fs10m) <(xxd -p -u -c 16 -s `echo '1024 * 8193' | bc` -l `echo '264' | bc` fs10m)
可以看到,并不是完全复制,对照 super block 结构 不一样的地方在于1).
:s_state[2]
,第一个为01 00
,第二个为00 00
。2).
:s_block_group_nr[2]
,第一个为00 00
,第二个为01 00
。
所以除了这两个块组的自身属性外,可以认为是完全复制。# 查看 (Block Group 0)super block # ➜ data git:(master) ✗ xxd -u -g1 -s `echo '1024 * 1' | bc` -l `echo '264' | bc` fs10m 00000400: 00 0A 00 00 00 28 00 00 00 02 00 00 5B 26 00 00 .....(......[&.. 00000410: F5 09 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00000420: 00 20 00 00 00 20 00 00 00 05 00 00 00 00 00 00 . ... .......... 00000430: F6 C3 0D 67 00 00 FF FF 53 EF 01 00 01 00 00 00 ...g....S....... 00000440: F6 C3 0D 67 00 00 00 00 00 00 00 00 01 00 00 00 ...g............ 00000450: 00 00 00 00 0B 00 00 00 80 00 00 00 38 00 00 00 ............8... 00000460: 02 00 00 00 03 00 00 00 C8 CC 95 B3 98 60 40 B5 .............`@. 00000470: A3 AC 66 22 AC 81 23 B2 00 00 00 00 00 00 00 00 ..f"..#......... 00000480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000004a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000004b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000004c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 27 00 ..............'. 000004d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000004e0: 00 00 00 00 00 00 00 00 00 00 00 00 C8 7F C3 0C ................ 000004f0: 07 50 46 64 86 E5 1F F8 EC C1 C7 9F 01 00 00 00 .PFd............ 00000500: 0C 00 00 00 00 00 00 00 ........
# 查看 (Block Group 1)super block # ➜ data git:(master) ✗ xxd -u -g1 -s `echo '1024 * 8193' | bc` -l `echo '264' | bc` fs10m 00800400: 00 0A 00 00 00 28 00 00 00 02 00 00 5B 26 00 00 .....(......[&.. 00800410: F5 09 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00800420: 00 20 00 00 00 20 00 00 00 05 00 00 00 00 00 00 . ... .......... 00800430: F6 C3 0D 67 00 00 FF FF 53 EF 00 00 01 00 00 00 ...g....S....... 00800440: F6 C3 0D 67 00 00 00 00 00 00 00 00 01 00 00 00 ...g............ 00800450: 00 00 00 00 0B 00 00 00 80 00 01 00 38 00 00 00 ............8... 00800460: 02 00 00 00 03 00 00 00 C8 CC 95 B3 98 60 40 B5 .............`@. 00800470: A3 AC 66 22 AC 81 23 B2 00 00 00 00 00 00 00 00 ..f"..#......... 00800480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00800490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 008004a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 008004b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 008004c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 27 00 ..............'. 008004d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 008004e0: 00 00 00 00 00 00 00 00 00 00 00 00 C8 7F C3 0C ................ 008004f0: 07 50 46 64 86 E5 1F F8 EC C1 C7 9F 01 00 00 00 .PFd............ 00800500: 0C 00 00 00 00 00 00 00 ........
总结 #
1).
: 一个 inode 里面会包含 数据块。如果是目录的 inode ,则 block 里面是目录形式的数据。如果是文件的 inode,则 block 里面是文件的数据。2).
: 对于多个块组的 super block 并不是完全复制,有些许属性会改变,比如块组号,块组状态。除了这两个属性,其他完全一致,所以大体上也可以认为是复制(拷贝)。
Reference #