文件系统是操作系统的一个重要组成部分,也有着举足轻重的地位。本系列文章主要讲述了linux ext4文件系统的一些实现原理。笔者参考了2.6.32.60的内核源代码。在写这篇的文章时,最新的内核已经去到了3.7.9。
ext4是替代ext2/3的Linux文件系统。从2.6.28版本开始,被正式认定进入稳定(stable)。本文主要介绍ext4文件系统在硬盘层面上的存储结构及原理。
读者对象:对Linux有一定基础,希望了解ext4底层原理,和它与ext2/3系统区别。
关键词汇
先回顾几个基本的概念,如果不是特别清楚下面几个概念的话,可以去google一下。
inode:索引节点
superblock:超级块
block:文件系统块
block group:文件系统块组
disk block:磁盘块(512字节)
block device:块设备
VFS:虚拟文件系统
EXT4存储结构
假如把整个超级块比如一本书,那么文件系统的工作就是把要记录的内容,按页码,行段记录在这本书里。这当然也包括书的目录了。我们使用dumpe2fs工具输出:
Reserved GDT blocks: 609 Blocks per group: 32768 Fragments per group: 32768 Inodes per group: 8128 Inode blocks per group: 508 Flex block group size: 16 Filesystem created: Mon May 14 13:30:51 2012 Last mount time: Sun Jan 6 18:51:16 2013 Last write time: Sun Jan 6 18:51:16 2013 Mount count: 282 Maximum mount count: -1 Last checked: Mon May 14 13:30:51 2012 Check interval: 0 (<none>) Lifetime writes: 20 GB Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root) First inode: 11 Inode size: 256 Required extra isize: 28 Desired extra isize: 28 Journal inode: 8 |
首先,映入眼帘的是该超级块的相关重要参数,比如inode大小,块组含块数,块组inode数目等等。这些数据是存在ext4_super_block,ext4_sb_info这两个结构体中,定义在ext4.h头文件里,它们不是本篇重要讨论的内容。我们只需要知道它们是存放一些超级块信息的结构体即可。
接下来,可以看到ext4硬盘上的存储结构:
Group 0: (Blocks 0-32767) [ITABLE_ZEROED] Checksum 0x7cf3, unused inodes 0 Primary superblock at 0, Group descriptors at 1-2 Reserved GDT blocks at 3-611 Block bitmap at 612 (+612), Inode bitmap at 628 (+628) Inode table at 644-1151 (+644) 2720 free blocks, 0 free inodes, 1383 directories Free blocks: 8888-8959, 9068, 9071-9135, 9144-9175, 9200-9207, 9213-9214, 9279, 9700-10120, 11823-11964, 12213-12870, 12879-13043, 13139-13254, 18432-19021, 22748-22975, 32549-32767 Free inodes: |
这是块组0的情况,它表明块组0由块号为0-32767这32768个块组成,超级块基本信息存在块0,块组描述符在块1-2,预留的块组描述符表在块3-611,块位图在块612中,inode位图在块628中,Inode表在块644-1151中,空闲的块有很多,空闲的inode没了。
接下来,我们将重点分析这句废话中每个词的含义
超级块基本信息:
我们在前面已经讲过了。顾名思义,不多解释。
块组描述符
在内核中就是结构体ext4_group_desc,它包括的内容为:块位图块号,inode位图块号,inode表块号,空闲块计数,自由块计数等等。
预留的块组描述符表
为以后要使用所留下来的空间。
块位图
这个就是一个块使用情况记录表。记录哪些块使用,哪些块未使用。它的原理就是对整个块组中0-32767这总共32768个块中作一个映射。根据一个bytes有8个位00000000,一个块有4096bytes也就是有4096*8=32768个位,这32768个位刚好对应了块组中32768的块。如果第N个块被使用了则标记第N位为1,否则为0。
inode位图
和上面的块位图一样,这个是inode的使用情况记录表。由超级块基本信息可以看到每个块组有8128个inode,这里对inode的映射原理和块位图也是一样,只不过 没有用满一个块。
inode表
inode表就是具体存放inode信息的地方。在ext4中,inode的大小为256字节(ext2/3中仅有它的一半,128字节),一个块可以存放16个inode,由于一个块组有8128 个inode,总共需要8128/16=508个块存放inode表。这个值可以在超级块基本信息中的Inode blocks per group中看到。
讲完了这些词的含义,我们对group 0有了初步的了解。那么group 1呢?
Group 1: (Blocks 32768-65535) [ITABLE_ZEROED] Checksum 0xbb99, unused inodes 0 Backup superblock at 32768, Group descriptors at 32769-32770 Reserved GDT blocks at 32771-33379 Block bitmap at 613 (+4294935141), Inode bitmap at 629 (+4294935157) Inode table at 1152-1659 (+4294935680) 598 free blocks, 0 free inodes, 648 directories Free blocks: 33424-33439, 33442-33443, 33564-33627, 33644-33647, 33652-33663, 33725-33871, 33878-33931, 33934-33973, 33976-33983, 34046-34047, 34176-34303, 36008, 36015, 36019, 36412, 40299-40415 Free inodes: |
我们看到group 1 中,primary superblock 变为了backup superblock,由于超级块基本信息对于文件系统至关重要,为了系统的健壮性,ext文件系统在每个块组中都进行了备份。ext4考虑到在每个块组中都备份有点多余,尤其是组描述符表所以就仅在块号以3,5,7为幂的块组上进行备份。
用个表格表示超级块中块组的结构:
ext4 超级块 | 块组描述符 | Reserved GDT Blocks | 数据块位图 | Inode位图 | inode 表 | 数据块 |
1 block | 若干blocks | 若干 blocks | 1 block | 1 block | 若干 | 好多好多块 |
inode | Purpose |
0 | Doesn't exist; there is no inode 0. |
1 | List of defective blocks. |
2 | Root directory. |
3 | ACL index. |
4 | ACL data. |
5 | Boot loader. |
6 | Undelete directory. |
7 | Reserved group descriptors inode. |
8 | Journal inode. |
11 | First non-reserved inode. Usually this is the lost+found directory. |
块寻址
ext4的块寻址已经改为48位。这种设计改动是为了支持更大的文件系统大小。EXT4使用了区段(extents)这个概念,取代了过去早期UNIX文件系统中(ext2/3)中低效的非直接块映射机制。区段和NTFS上的cluster有点类似,它们都是选定了一个特定的块地址并把数个块组合一个区间。一个文件如果是碎片化的,那么就意味它着拥有多个区段,ext4会尽它自己的努力保持文件连续。
这种新的块寻址策略导致了先前工具的大部分问题。举个列子:
[root@localhost Desktop]# stat math.c File: `math.c' Size: 1477 Blocks: 8 IO Block: 4096 regular file Device: fd00h/64768d Inode:420402 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2013-01-05 15:07:11.815541582 +0800 Modify: 2012-08-20 13:40:02.496797954 +0800 Change: 2012-12-30 11:28:54.751357610 +0800
|
由上面得到文件math.c的Inode号为420402
[root@localhost Desktop]# istat /dev/mapper/VolGroup-lv_root 420402 inode: 420402 Allocated Group: 51 Generation Id: 1062005310 uid / gid: 0 / 0 mode: rrw-r--r-- Flags: size: 1477 num of links: 1
Inode Times: Accessed: 2013-01-05 15:07:11 (CST) File Modified: 2012-08-20 13:40:02 (CST) Inode Modified: 2012-12-30 11:28:54 (CST)
Direct Blocks: 127754
|
由上面的命令结果可以看到,Inode位于节点上块组51上,留意上面命令最下面有Direct Blocks这一行,这一行写着127754。在ext4的文件系统中,由于direct block映射的块寻址机制被取代,而采取的是extent区段树的块寻址。这个地方的值基本上是无效的。127754这个值十六进制表示为0x1f30a,我们在稍后的讨论这个值的来源。
我们知道了math.c这个文件的inode号码为420402,那么怎样知道它数据块是拿一个呢?
由前面的内容我们知道,每个块可以存16个inode,那么420402则在第420402/16=26275.125个块中,也就是位于第26275个块的第二个位置。每个块组有508个inode块,那么26725/508=51.72可以得知,位于块组51号之中,这个值可以在我们之前istat中可以验证。
那么具体是51块组中的哪个块呢?我们先确定这个inode块是在块组中的第几个块。因为每个块组有508个inode块,51块组前面共有51*508=25908个块。第26275个inode块在51块组中排在26275-25908=367的位置。查看51块组的描述:
Group 51: (Blocks 1671168-1703935) [ITABLE_ZEROED] Checksum 0x5ffd, unused inodes 0 Block bitmap at 1572867 (+4294868995), Inode bitmap at 1572883 (+4294869011) Inode table at 1574420-1574927 (+4294870548) 34 free blocks, 1 free inodes, 541 directories Free blocks: 1672899, 1673339, 1673344, 1674035, 1674054, 1674062, 1674077, 16 74334, 1674353-1674354, 1674423, 1675259, 1675754-1675755, 1675763, 1675860-1675 861, 1675867, 1675979, 1676183, 1676287, 1676367, 1676507, 1676526-1676527, 1676 567, 1676711, 1676743, 1676924-1676925, 1676934, 1691649, 1691658, 1691707 Free inodes: 422608 |
看到inode的起点位于1574420,由此,我们想要找的inode信息的块就存在于1574787inode块中的第二个。
可以使用blkcat查看1574787的内容,我们用vi切换到16进制模式打开如下:
0000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000080: 1c00 0000 4430 c1a7 c8f1 733d fc8a 6cb1 ....D0....s=..l. 0000090: 58a8 b04f 04a0 6538 0000 0000 0000 02ea X..O..e8........ 00000a0: 0706 3c00 0000 0000 2200 0000 0000 0000 ..<....."....... 00000b0: 7365 6c69 6e75 7800 0000 0000 0000 0000 selinux......... 00000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000d0: 0000 0000 0000 0000 0000 0000 7379 7374 ............syst 00000e0: 656d 5f75 3a6f 626a 6563 745f 723a 6164 em_u:object_r:ad 00000f0: 6d69 6e5f 686f 6d65 5f74 3a73 3000 0000 min_home_t:s0... 0000100: a481 0000 c505 0000 1fd1 e750 f6b4 df50 ...........P...P 0000110: b2cd 3150 0000 0000 0000 0100 0800 0000 ..1P............ 0000120: 0000 0800 0100 0000 0af3 0100 0400 0000 ................ 0000130: 0000 0000 0000 0000 0100 0000 d64a 1c00 .............J.. 0000140: 0000 0000 0000 0000 0000 0000 0000 0000 ................ |
我们知道inode的size大小为256字节,那么第二个inode的起始位置也就是256=0x100处。
这个时候,我们看下inode的数据结构:
位置 | 值 | 名称 | 描述 |
0x0 | __le16 | i_mode | 文件模式 |
0x2 | __le16 | i_uid | 所有者UID. |
0x4 | __le32 | i_size_lo | 文件大小. |
0x8 | __le32 | i_atime | 读取时间. |
0xC | __le32 | i_ctime | Inode修改时间 |
0x10 | __le32 | i_mtime | 文件修改时间. |
0x14 | __le32 | i_dtime | 删除时间 |
0x18 | __le16 | i_gid | GID. |
0x1A | __le16 | i_links_count | 硬链接计数. |
0x1C | __le32 | i_blocks_lo | 块计数(512字节) |
0x20 | __le32 | i_flags | 文件标识(ext4使用extent需要标记0x80000) |
... | |||
0x28 | __le32 | i_block[EXT4_N_BLOCKS=15] | 块映射(ext2/3)或区段树(ext4) |
... |
我们按照表中的结构,对照上面的块码:
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x81a4 | i_mode | 文件模式 |
0x2 | 0x0000 | i_uid | 所有者UID. |
0x4 | 0x0000 05c5 | i_size_lo | 文件大小. |
0x8 | 0x50e7 d11f | i_atime | 读取时间. |
0xC | 0x50df b4f6 | i_ctime | Inode修改时间 |
0x10 | 0x5031 b2cd | i_mtime | 文件修改时间. |
0x14 | 0x0000 0000 | i_dtime | 删除时间 |
0x18 | 0x0000 | i_gid | GID. |
0x1A | 0x0001 | i_links_count | 硬链接计数. |
0x1C | 0x0000 0008 | i_blocks_lo | 块计数(512字节) |
0x20 | 0x0080 0000 | i_flags | 文件标识(ext4使用extent需要标记0x80000) |
... | |||
0x28 | ... | i_block[EXT4_N_BLOCKS=15] | 块映射(ext2/3)或区段树(ext4) |
... |
细心的同学会发现大小的顺序是倒过来的,这是因为__lexx类型,le是little endian小端开始的缩写,意思就是从小到大的顺序。我们看到文件的大小为0x5c5=1477,这说明我们找的正是math.c的inode。
因为ext4 使用区段去代替了块映射去查找文件的内容。从40-99这60个字节过去是用作块映射的,如今用作存储extent信息。extent结构体有12字节的大小,反应快的同学马上会说,那么一个inode可以存放最多5个extent。然而这是不对的,因为前12个字节(40-51)被段头(extent header)所占据,所以,一个inode中的区段数最多只能是4。
现在,我们重点开始讲区段树(extent tree)
在ext4中,区段树取代了旧式的逻辑块映射。这是因为在老的模式中,分配连续的1000个块需要映射这1000个块的地址。但使用了区段,只需要映射一个区段并把区段的长度标记为1000(ee_len=1000)。如果起用了flex_bg的功能,一个区段可以分配一个很大的文件,这降低了元数据的大小,也提高了硬盘的效率。inode必须使用区段标记0x80000开启区段的功能。
区段的结构是树形的。每个树节点的起始为:struct ext4_extent_header
(这是一个结构体,我们接下来会给大家描述它的内容)。如果一个节点是树的内部节点(即eh.eh_depth>0),那么eh.eh_entries的指针将指向struct ext4_extent_idx;每个这些索引都指向一个块,块中包含更多的区段树中的节点。如果节点是树的叶子节点(eh.eh_depth=0),那么eh.eh_entries的指针将指向struct ext4_extent;这些结构体中指向文件的数据块。区段树的根节点存在inode.i_block,也就是我们在前面讨论的从40-99的那60个字节里。
说了这么多,我们还是赶紧看看extent的结构吧;
首先出场的是段头(extent header)
偏移 | 大小 | 名称 | 描述 |
0x0 | __le16 | eh_magic | 幻数magic number, 0xF30A. |
0x2 | __le16 | eh_entries | 区段数. |
0x4 | __le16 | eh_max | 最大的区段数. |
0x6 | __le16 | eh_depth | 段节点在段树中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。 |
0x8 | __le32 | eh_generation | 暂不讨论 |
同样的,对照我们的实际数据看看
偏移 | 大小 | 名称 | 描述 |
0x0 | 0xf30a | eh_magic | 幻数magic number, 0xF30A. |
0x2 | 0x0001 | eh_entries | 区段数. |
0x4 | 0x0004 | eh_max | 最大的区段数. |
0x6 | 0x0000 | eh_depth | 段节点在段树中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。 |
0x8 | 0x0000 0000 | eh_generation | 暂不讨论 |
接下来我们先看struct ext4_extent_idx
,这个结构在前面我们有提到过,用于extent树的内部节点。
偏移 | 大小 | 名称 | 描述 |
0x0 | __le32 | ei_block | 逻辑块号. |
0x4 | __le32 | ei_leaf_lo | 区段树中下一层的区段节点块地址(低32位),可以指向叶子节点或者内部节点。 |
0x8 | __le16 | ei_leaf_hi | 上一栏的高16位地址 |
0xA | __u16 | ei_unused | 未使用 |
我们接着看struct ext4_extent,叶子节点的结构体
偏移 | 大小 | 名称 | 描述 |
0x0 | __le32 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | __le16 | ee_len | 区段内包含的块数. |
0x6 | __le16 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | __le32 | ee_start_lo | 此区段所指向的块号(低32位) |
对照我们的实际数据看看
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0000 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x001c 4ad6 | ee_start_lo | 此区段所指向的块号(低32位) |
由上表可以看到,因为我们的文件较小,这里作为叶子节点直接指向了文件数据块。数据块号为0x001c4ad6=1854166。我们使用命令查看块中的内容:
[root@localhost Desktop]# blkcat /dev/mapper/VolGroup-lv_root 1854166 #include <stdlib.h> #include <math.h> ... |
呵呵,可以看到,这就是我们的math.c文件。
思考:
请读者找一个大于4k的文件,看看能不能找到它的数据块。
删除文件
执行rm后删除文件,数据块并没有被清除,inode被释放,有下面3项会改变: 1. 文件大小被置为0
2. 段头中的区段值被设为0
3. 区段被清空
清空了区段意味着我们会失去文件起始物理块的地址和区段的长度。也就是说,在inode中已经不存在元数据可以帮我们恢复文件。这种行为和ext3回收inode时清除inode中的块指针很相似。这样就意味着我们只能靠传统的file-carving去恢复文件。
还记得在上一章中,我们提到过的结构体struct ext4_extent_idx
。这个结构体表示在extent tree中的节点。 我们在前面的章节已经阐述过,ext4使用extent取代了传统的block映射方式。我们的案例中只展示了只有一个extent的情况。本篇文章将介绍多个extent情况下的具体细节。
在本文中,我们选取了文件/var/log/messages,它是系统日志的记录文件,由于它的角色特殊,时间长了会造成给很多的碎片。我们还是先看看他的inode,方法和(一)中描述的一致,在此不重复了。
0000c00: 8081 0000 d036 0000 814b ee50 7f4b ee50 .....6...K.P.K.P 0000c10: 7f4b ee50 0000 0000 0000 0100 2000 0000 .K.P........ ... 0000c20: 0000 0800 0100 00000af30400 0400 0000 ................ 0000c30: 0000 0000 0000 0000 0100 0000 58c2 0b00 ............X... 0000c40: 0100 0000 0100 0000 e599 1b00 0200 0000 ................ 0000c50: 0100 0000 41e6 2f00 0300 0000 0100 0000 ....A./......... 0000c60: b878 1c00 275f ea72 0000 0000 0000 0000 .x..'_.r........ 0000c70: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000c80: 1c00 0000 c018 94d1 c018 94d1 c0e5 ea86 ................ 0000c90: 9bea e850 d0a4 fcb4 0000 0000 0000 02ea ...P............ 0000ca0: 0706 4000 0000 0000 1f00 0000 0000 0000 ..@............. 0000cb0: 7365 6c69 6e75 7800 0000 0000 0000 0000 selinux......... 0000cc0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000cd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000ce0: 7379 7374 656d 5f75 3a6f 626a 6563 745f system_u:object_ 0000cf0: 723a 7661 725f 6c6f 675f 743a 7330 0000 r:var_log_t:s0..
|
从0x0af3开始,这是extent header
起始的标记,我们还是像(一)那样,对照着表看
偏移 | 大小 | 名称 | 描述 |
0x0 | 0xf30a | eh_magic | 幻数magic number, 0xF30A. |
0x2 | 0x0004 | eh_entries | 区段数. |
0x4 | 0x0004 | eh_max | 最大的区段数. |
0x6 | 0x0000 | eh_depth | 段节点在段数中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。 |
0x8 | 0x0000 | eh_generation | 暂不讨论 |
因为这里看到depth为0,说明extent中指向的是数据块。在extent header中接下来的将extent是我们把接下来的数据对应到它的表中
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0000 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x000b c258 | ee_start_lo | 此区段所指向的块号(低32位) |
接下来的12个字节
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0001 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x001b 99e5 | ee_start_lo | 此区段所指向的块号(低32位) |
再接下来的12个字节
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0002 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x00f2 e641 | ee_start_lo | 此区段所指向的块号(低32位) |
最后的12个字节
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0003 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x001c 78b8 | ee_start_lo | 此区段所指向的块号(低32位) |
可以通过使用blkcat看到4个块刚好凑成了messages文件,疑心重的同学可以把4个块拼成一个文件,用md5sum比较一下。这里我们并没有想看到extent中的内部节点情况。没关系,我们系统日志文件时会随着时间增长。正在笔者撰写此文时,messages已经变大了,并且超过了16k的大小,也就是超出了4个块。这时候我们看看messages的inode信息,方法不重复了。
0000c00: 8081 0000 4f51 0000 eb84 ef50 ea84 ef50 ....OQ.....P...P 0000c10: ea84 ef50 0000 0000 0000 0100 3800 0000 ...P........8... 0000c20: 0000 0800 0100 00000af3 0100 0400 0100 ................ 0000c30: 0000 0000 0000 0000 7c03 1c00 0000 0b00 ........|....... 0000c40: 0100 0000 0100 0000 e599 1b00 0200 0000 ................ 0000c50: 0100 0000 41e6 2f00 0300 0000 0100 0000 ....A./......... 0000c60: b878 1c00 275f ea72 0000 0000 0000 0000 .x..'_.r........ 0000c70: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000c80: 1c00 0000 c80e c097 c80e c097 accd 3948 ..............9H 0000c90: 9bea e850 d0a4 fcb4 0000 0000 0000 02ea ...P............ 0000ca0: 0706 4000 0000 0000 1f00 0000 0000 0000 ..@............. 0000cb0: 7365 6c69 6e75 7800 0000 0000 0000 0000 selinux......... 0000cc0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000cd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000ce0: 7379 7374 656d 5f75 3a6f 626a 6563 745f system_u:object_ 0000cf0: 723a 7661 725f 6c6f 675f 743a 7330 0000 r:var_log_t:s0..
|
经过一段时间的练习,同学们应该不需要对照表来识辨这些16进制码的含义了,如果不熟练的话,可以回过头多看几遍。我们看到这个inode较之前有了变化。extent的区段数变成了1,区段树的深度变成了1。区段树深度为1,这说明非叶子节点。
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0000 | ei_block | 逻辑块号. |
0x4 | 0x001c 037c | ei_leaf_lo | 区段树中下一层的区段节点块地址(低32位),可以指向叶子节点或者内部节点。 |
0x8 | 0x0000 | ei_leaf_hi | 上一栏的高16位地址 |
0xA | 0x000b | ei_unused | 未使用 |
使用blkcat查看文件系统块1835900的内容,
0000000: 0af3 0e00 5401 0000 0000 0000 0000 0000 ....T........... 0000010: 0100 0000 58c2 0b00 0100 0000 0100 0000 ....X........... 0000020: e599 1b00 0200 0000 0100 0000 41e6 2f00 ............A./. 0000030: 0300 0000 0100 0000 b878 1c00 0400 0000 .........x...... 0000040: 0100 0000 2d8b 1b00 0500 0000 0100 0000 ....-........... 0000050: 9a79 1c00 0600 0000 0100 0000 0d82 1b00 .y.............. 0000060: 0700 0000 0100 0000 1182 1b00 0800 0000 ................ 0000070: 0100 0000 1382 1b00 0900 0000 0100 0000 ................ 0000080: 1682 1b00 0a00 0000 0100 0000 1882 1b00 ................ 0000090: 0b00 0000 0300 0000 034e 1c00 0e00 0000 .........N...... 00000a0: 0200 0000 074e 1c00 1000 0000 0400 0000 .....N.......... 00000b0: e082 0c00 0000 0000 0000 0000 0000 0000 ................
|
从上面的数据,我们可以看到有0x000e个区段,也就是在extent header后有14个extent或者extent ixd的结构体。接着看到extent最大数为0x0154=340,这个数字是怎么来的呢?我们知道在一个inode里面,这个值是4,那是因为('extent space' - 'extent header size') / 'extent size' 即 (60-12)/12=4,那么在这里也一样,只不过60这里要变为4096,因为我们不在inode中,而是在一个块中,即4096-12=4084,4084/12即340.333,最后还剩4个字节浪费了。再接着,是树的深度,也就是0x0000表明是叶子节点。那么我们知道,这个文件由着14个block的块组成。有兴趣的同学可以自己把文件dump出来拼一下。呵呵。