Ext2
文件系统 |
---|
虚拟文件系统 |
磁盘文件系统 |
CD/DVD 文件系统 |
Network 文件系统 |
Flash 文件系统 |
Second Extended Filesystem(ext2fs)是对原始“扩展文件系统(Extended Filesystem)”的重写,因此也基于“inodes”的概念从20世纪90年代初到21世纪初,Ext2作为Linux事实上的文件系统存在了近十年,目前它被日志文件系统Ext3和ReiserFS取代。 它具有对UNIX所有权/访问权限、符号和硬链接以及类UNIX操作系统中常见的其他属性的本机支持。 从组织上讲,它将磁盘空间分为称为 “block groups” 的组。 有了这些组,数据就可以分布在整个磁盘上,这有助于最大限度地减少磁头移动以及碎片的影响。 此外,某些(如果不是全部)组需要包含重要数据的备份,这些数据可用于在发生灾难时重建文件系统。
注意: 这里的大部分信息是基于Dave Poirier在ext2-doc项目上所做的工作 (请参阅 链接部分),该项目是根据 GNU自由文档许可证 慷慨发布的。 下次见到他时一定要给他买杯啤酒。
基本概念
重要提示: 除非另有说明,否则所有值均为little-endian
什么是Block?
Ext2文件系统将磁盘空间划分为连续空间的逻辑块。 块的大小不必与文件系统所在磁盘的扇区大小相同。 块的大小可以通过读取Superblock中从字节24开始的字段来确定。
什么是Block Group?
多个块与索引节点一起被划分为“Block Group” 这些只不过是连续的区块组。
每个块组都保留一些用于特殊目的的块,例如:
- 组内空闲/分配blocks的位图
- 组内已分配inodes的位图
- 属于该组的inode结构表
- 根据使用的Ext2版本,部分或所有块组还可能包含[#Superblock|Superblock]]和[#block_Group_Descriptor_Table |block Group Descriptor Table]]的备份副本。
什么是Inode?
inode是磁盘上的一种结构,表示文件,目录,符号链接等。 inode不包含它们表示的文件/目录/等的数据。 相反,它们链接到实际包含数据的块。 这使得inode本身有一个定义良好的大小,可以将它们放置在易于索引的数组中。 每个block group都有一个它负责的inode数组,反之文件系统中的每个inode都属于其中一个表(以及一个这样的block group)。
Superblock
实现Ext2驱动程序的第一步是查找,提取和解析superblock。 Superblock包含有关文件系统布局的所有信息,还可能包含其他重要信息,如创建文件系统时使用了哪些可选功能。 完成Superblock后,下一步是查看Block_Group_Descriptor_Table
定位Superblock
Superblock始终位于卷开头的1024字节处,长度正好为1024字节。 例如,如果磁盘使用512字节的扇区,则Superblock将从LBA 2开始,并将占用所有扇区2和3。
确定Block Group的数量
从Superblock中,提取每个块的大小、索引节点总数、块总数、每个块组的块数以及每个块组中的索引节点数。 根据这些信息,我们可以通过以下方法推断Block Group的数量:
- 向上取整总数除以每个Block Group的Block数
- 向上取整inodes总数除以每个Block Group的inodes数
- 两者都可以(并相互核对)
基本Superblock字段
这些字段出现在所有版本的Ext2中
开头
Byte |
结尾
Byte |
大小
in Bytes |
字段说明 |
---|---|---|---|
0 | 3 | 4 | 文件系统中inode的总数 |
4 | 7 | 4 | 文件系统中的Block总数 |
8 | 11 | 4 | 为超级用户预留的块数(参见偏移量80) |
12 | 15 | | 未分配的块总数 | |
16 | 19 | 4 | 未分配inodes的总数 |
20 | 23 | 4 | 包含Superblock的Block的Block号 |
24 | 27 | 4 | log2 (block size) - 10. (换句话说,要向左移动1,024以获得块大小的数字) |
28 | 31 | 4 | log2 (fragment size) - 10. (换句话说,将1024向左移动以获得片段大小的数字) |
32 | 35 | 4 | 每个Block Group的Block个数 |
36 | 39 | 4 | 每个Block Group中的片段数 |
40 | 43 | 4 | 每个Block Group中的inodes点数 |
44 | 47 | 4 | 上次挂载时间(单位:Posix时间) |
48 | 51 | 4 | 最后写入时间 (在 Unix_time POSIX时间) |
52 | 53 | 2 | 自上次一致性检查以来已装入卷的次数(fsck) |
54 | 55 | 2 | 必须进行一致性检查(fsck)之前允许的挂载次数 |
56 | 57 | 2 | Ext2签名 (0xef53),用于帮助确认一个卷上是否存在Ext2 |
58 | 59 | 2 | 见下文]) |
60 | 61 | 2 | 检测到错误时怎么办(见下文) |
62 | 63 | 2 | 版本的次要部分 (与下面的主要部分结合以构建完整版本字段) |
64 | 67 | 4 | 上次一致性检查([fsck)的POSIX时间 |
68 | 71 | 4 | 强制一致性检查(fsck),间隔poix time |
72 | 75 | 4 | 在此卷上创建文件系统的操作系统ID ( 见下文) |
76 | 79 | 4 | 版本的主要部分(与上面的次要部分相结合以构建完整的版本字段) |
80 | 81 | 2 | 可以使用预留块的用户ID |
82 | 83 | 2 | 可以使用保留块的组ID |
File System States
Value | 状态描述 |
---|---|
1 | 文件系统被清除 |
2 | 文件系统有错误 |
Error Handling Methods
值 | 要采取的行动 |
---|---|
1 | 忽略错误 (继续) |
2 | 以只读方式重新安装文件系统 |
3 | 内核死机(Kernel panic) |
Creator Operating System IDs
值 | 操作系统 |
---|---|
0 | Linux |
1 | GNU HURD |
2 | MASIX (由rémy Card开发的操作系统,ext2的开发者之一) |
3 | FreeBSD |
4 | 其它 "Lites" (BSD4.4-Lite derivatives such as NetBSD, OpenBSD, XNU/Darwin等。) |
扩展Superblock字段
仅当主要版本 (在基本超级块字段中指定) 大于或等于1时,才存在这些字段。
开头
Byte |
结尾
Byte |
大小
以字节为单位 |
字段说明 |
---|---|---|---|
84 | 87 | 4 | 文件系统中的第一个非保留inode。(在 <1.0版本中,此固定为11) |
88 | 89 | 2 | 每个inode结构的大小(字节)。(在<1.0版本中,固定为128) |
90 | 91 | 2 | 该Superblock所属的Block Group(如有备份副本) |
92 | 95 | 4 | 存在的可选功能 (不需要读取或写入,但通常会导致性能提高的功能。见下文) |
96 | 99 | 4 | 见下文]) |
100 | 103 | 4 | 如果不支持,卷必须以只读方式挂载的功能见下文) |
104 | 119 | 16 | 文件系统ID (blkid输出的是什么) |
120 | 135 | 16 | 卷名(C样式字符串:以0字节结尾的字符) |
136 | 199 | 64 | 路径卷最后挂载到(C样式字符串:以0字节结尾的字符) |
200 | 203 | 4 | 使用的压缩算法 (请参见上面的必需功能) |
204 | 204 | 1 | 要为文件预分配的块数 |
205 | 205 | 1 | 为目录预分配的块数 |
206 | 207 | 2 | (未使用) |
208 | 223 | 16 | 日志ID(与上面的文件系统ID样式相同) |
224 | 227 | 4 | Journal inode |
228 | 231 | 4 | Journal device |
232 | 235 | 4 | 孤立inode列表头部 |
236 | 1023 | X | (未使用) |
Optional Feature Flags
这些是实现需要支持的可选功能,但为支持它们的实现提供了性能或可靠性增益。
Flag Value | Description |
---|---|
0x0001 | 预分配一定数量的(是否连续?)块(参见Superblock中的字节205)在创建新块时复制到目录(以减少碎片?) |
0x0002 | AFS服务inode存在 |
0x0004 | 文件系统有一个日志(Ext3) |
0x0008 | Inodes具有扩展属性 |
0x0010 | 文件系统可以为更大的分区调整自身大小 |
0x0020 | 目录使用散列索引 |
Required Feature Flags
为了正确地从文件系统读取或写入文件系统,实现需要支持这些功能(如果存在于文件系统上)。
标志值 | 描述 |
---|---|
0x0001 | 使用压缩 |
0x0002 | 目录条目包含类型字段 |
0x0004 | 文件系统需要重播日志 |
0x0008 | 文件系统使用日志设备 |
Read-Only Feature Flags
这些功能 (如果存在于文件系统上) 是实现写入文件系统所必需的,但不需要从文件系统读取。
标志值 | 描述 |
---|---|
0x0001 | 稀疏超级块和组描述符表 |
0x0002 | 文件系统使用64位文件大小 |
0x0004 | 目录内容以二叉树的形式存储 |
Block Group Descriptor Table
块组描述符表包含文件系统内每个块组的描述符。 描述了文件系统中的块组数,以及相应的块组描述符表中的条目数[#Determining_the_Number_of_Block_Groups|如上]]。 每个描述符包含关于该组的重要数据结构位于何处的信息。
定位Block Group描述符表
该表位于超级块后面的块中。 因此,如果块大小(从超级块中的字段确定)是每个块1024字节,则块组描述符表格将从块2开始。 对于任何其他块大小,它将从块1开始。 请记住,块从0开始编号,块编号通常与物理块地址不对应。
Block Group Descriptor
Block Group描述符包含关于该Block Group的重要数据结构位于何处的信息。
开头
Byte |
结尾
Byte |
大小
以字节为单位 |
字段描述 |
---|---|---|---|
0 | 3 | 4 | 块使用位图的块地址 |
4 | 7 | 4 | inode usage位图的块地址 |
8 | 11 | 4 | inode表的起始块地址 |
12 | 13 | 2 | 组中未分配的块数 |
14 | 15 | 2 | 组中未分配的索引节点数 |
16 | 17 | 2 | 组内目录数 |
18 | 31 | X | (未使用) |
Inodes
像块一样,每个inode都有一个数字地址。 需要特别注意的是,与块地址不同,“inode地址从1开始”。
对于主要版本1之前的ext2版本,inode 1到10是保留的,应该处于已分配状态。 从版本1开始,通过超级块中的字段指示第一个非保留inode。 在保留的索引节点中,数字2主观上最重要,因为它用于根目录。
对于版本0的ext2文件系统,inode的大小固定为128,对于版本1的文件系统,索引节点的大小由超级块中的字段指定。 所有inode驻留在属于块组的inode表中。 因此,查找inode只需确定它所属的块组,并为该块组的inode表编制索引。
确定哪个Block Group包含Inode
根据信息节点地址(请记住,它们从1开始),我们可以使用以下公式确定该信息节点所在的组:
block group = (inode – 1) / INODES_PER_GROUP
其中INODES_PER_GROUP是超级块中的一个字段
在块组内查找索引节点
一旦我们知道inode驻留在哪个组中,我们就可以通过首先检索挡路组的索引节点表的起始地址来查找实际的索引节点(请参见上面的Block_Group_Descriptor)。 我们的inode在这个区块组的inode表的索引可以通过使用公式来确定:
index = (inode – 1) % INODES_PER_GROUP
其中%表示模运算和INODES_PER_组是超级块中的一个字段 (与用于确定inode属于哪个Block Group的字段相同)。
接下来,我们必须确定哪个块包含我们的inode。 这是通过以下方式实现的:
containing block = (index * INODE_SIZE) / BLOCK_SIZE
其中,如果版本<1,则INODE_SIZE固定在128时,或者如果VERSION>=1.0,则由超级块中的字段定义,并且BLOCK_SIZE由超级块中的字段定义。
最后,根据需要进行掩码和移位,以仅从包含块中提取inode数据。
读取索引节点的内容
每个inode包含12个direct pointers、一个singly indirect pointer、一个doubly indirect block pointer和一个triply indirect pointer。 direct(直接)空间“溢出” 到singly indirect,后者溢出到doubly indirect空间,后者溢出到triply indirect 空间。
Direct Block Pointers: 有12个直接块指针。 如果有效,则该值为非零值。 每个指针是包含此inode数据的块地址。
Singly Indirect Block Pointer: 如果文件需要12个以上的块,则会分配一个单独的块来存储存储其内容所需的其余数据块的块地址。 这个单独的块被称为间接块(indirect block),因为它在inode及其数据之间添加了额外的步骤(间接级别)。 块中存储的块地址均为32位元,此块中存储地址的容量是块大小的函数。 此间接块的地址存储在inode中的“Singley indirect block Pointer”字段中。
Doubly Indirect Block Pointer: 如果文件的块多于12个直接指针和间接块所能容纳的块,则使用双间接块(double indirect block)。 双间接块是上述间接块的扩展,仅现在我们在inode和数据块之间有两个中间块。 inode结构有一个“双间接块指针”字段,必要时指向该块。
Triply Indirect Block Pointer: 最后,如果文件需要更多空间,可以使用三重间接挡路。 同样,这是双重间接块的扩展。 所以,三重间接块包含两个间接块的地址,其中包含单个间接块的地址,其中包含数据块的地址。 Inode结构有一个“三重间接块指针”字段,它指向这个块(如果存在)。
从维基百科查看这张图片 很好地说明了上面描述的内容。
Inode Data Structure
开头
Byte |
结尾
Byte |
大小
以字节为单位 |
字段说明 |
---|---|---|---|
0 | 1 | 2 | 类型和权限 ( 见下文) |
2 | 3 | 2 | User ID |
4 | 7 | 4 | 以字节为单位的低32位大小 |
8 | 11 | 4 | 上次访问时间(单位:Posix时间) |
12 | 15 | 4 | 创建时间 (在 Unix_time POSIX时间) |
16 | 19 | 4 | 上次修改时间(单位:分钟)[1]) |
20 | 23 | 4 | 删除 (Posix时间) |
24 | 25 | 2 | Group ID |
26 | 27 | 2 | 到此inode的硬链接 (目录条目) 的计数。 当该值达到0时,数据块被标记为未分配。 |
28 | 31 | 4 | 该inode使用的磁盘扇区(非ext2块)计数,不计算实际inode结构和链接到inode的目录项。 |
32 | 35 | 4 | Flags (see below) |
36 | 39 | 4 | Operating System Specific value #1 |
40 | 43 | 4 | Direct Block Pointer 0 |
44 | 47 | 4 | Direct Block Pointer 1 |
48 | 51 | 4 | Direct Block Pointer 2 |
52 | 55 | 4 | Direct Block Pointer 3 |
56 | 59 | 4 | Direct Block Pointer 4 |
60 | 63 | 4 | Direct Block Pointer 5 |
64 | 67 | 4 | Direct Block Pointer 6 |
68 | 71 | 4 | Direct Block Pointer 7 |
72 | 75 | 4 | Direct Block Pointer 8 |
76 | 79 | 4 | Direct Block Pointer 9 |
80 | 83 | 4 | Direct Block Pointer 10 |
84 | 87 | 4 | Direct Block Pointer 11 |
88 | 91 | 4 | 单间接块指针 (指向数据块指针列表的块) |
92 | 95 | 4 | 双间接块指针(指向一个块,该块是指向单间接块的块指针列表) |
96 | 99 | 4 | 三重间接块指针(指向块,即指向双间接分块的挡路指针列表) |
100 | 103 | 4 | Generation number (主要用于NFS) |
104 | 107 | 4 | 在Ext2版本0中,此字段是保留的。在版本>=1中,扩展属性块(文件ACL)。 |
108 | 111 | 4 | ext2版本0中,此字段为保留字段。 在版本> = 1中,如果是文件,则文件大小的最高32位 (如果设置了功能位),如果是目录,则目录ACL |
112 | 115 | 4 | 片段的块地址 |
116 | 127 | 12 | Operating System Specific Value #2 |
Inode类型和权限
类型值
十六进制 |
类型说明 |
---|---|
0x1000 | FIFO |
0x2000 | Character device |
0x4000 | Directory |
0x6000 | Block device |
0x8000 | Regular file |
0xA000 | Symbolic link |
0xC000 | Unix socket |
权限
值 (十六进制) |
权限
八进制值 |
权限描述 |
---|---|---|
0x001 | 00001 | Other—execute permission |
0x002 | 00002 | Other—write permission |
0x004 | 00004 | Other—read permission |
0x008 | 00010 | Group—execute permission |
0x010 | 00020 | Group—write permission |
0x020 | 00040 | Group—read permission |
0x040 | 00100 | User—execute permission |
0x080 | 00200 | User—write permission |
0x100 | 00400 | User—read permission |
0x200 | 01000 | Sticky Bit |
0x400 | 02000 | Set group ID |
0x800 | 04000 | Set user ID |
Inode Flags
标志值 | 描述 |
---|---|
0x00000001 | Secure deletion (未使用) |
0x00000002 | 删除时保留一份数据副本(未使用) |
0x00000004 | 文件压缩(未使用) |
0x00000008 | 同步更新-新数据立即写入磁盘 |
0x00000010 | 不可变文件(内容无法更改) |
0x00000020 | 仅追加 |
0x00000040 | 文件不包含在 “转储(dump)” 命令中 |
0x00000080 | 上次访问时间不应更新 |
... | (保留) |
0x00010000 | 哈希索引目录 |
0x00020000 | AFS目录 |
0x00040000 | 日志文件数据 |
OS Specific Value 1
操作
系统 |
它们如何使用这个领域 |
---|---|
Linux | (reserved) |
HURD | "translator"? |
MASIX | (reserved) |
OS Specific Value 2
操作
系统 |
它们如何使用这个字段 | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Linux |
| ||||||||||||||||||||||||||||
HURD |
| ||||||||||||||||||||||||||||
MASIX |
|
Directories
目录是inodes,其中包含一些“条目”作为其内容。 这些条目只不过是一个name/inode节点对。 例如,与根目录相对应的inode可能具有一个名称为 “etc” 且inode值为50的条目。 目录inode以链表方式将这些条目存储在其内容块中。
根目录是inode 2。
目录条目的总大小可能比名称的长度所显示的要长 (名称可能不会跨越到记录的末尾),并且记录必须与4字节边界对齐。 目录项也不允许跨越文件系统上的多个块,因此目录项之间可能有空白。 但是,目录条目之间不允许有空格,因此通过增加记录长度以包括空格,任何可能的空格都将用作前面记录的一部分。 Empty空间也可以等效地由inode数为0的单独目录项标记,指示应跳过该目录项。
Directory Entry
开头
Byte |
结尾
Byte |
Size
以字节为单位 |
字段描述 |
---|---|---|---|
0 | 3 | 4 | Inode |
4 | 5 | 2 | 该条目的总大小(包括所有子字段) |
6 | 6 | 1 | 名称长度最小有效8位 |
7 | 7 | 1 | Type indicator (仅当设置了“directory entries have file type byte”的特征位时,否则这是名称长度的最高有效8位) |
8 | 8+N-1 | N | Name characters |
Directory Entry Type Indicators
值 | 类型说明 |
---|---|
0 | 未知类型 |
1 | 常规文件 |
2 | 目录 |
3 | Character device |
4 | Block device |
5 | FIFO |
6 | Socket |
7 | Symbolic link (soft link) |
快速摘要
如何读取索引节点
- 读取超级块以查找每个块的大小、每个组的块数、每个组的Inodes数以及第一个组的起始Block(Block Group Descriptor Table)。
- 确定inode属于哪个块组。
- 读取与包含要查找的inode的块组相对应的块组描述符。
- 从Block Group Descriptor中提取Block Group的inode表的位置。
- 确定inode表中inode的索引。
- 索引inode table(考虑非标准inode大小)。
目录条目信息和文件内容位于inode指向的数据块内。
如何读取根目录
根目录的inode定义为始终为2。读取/解析inode 2的内容。
另见
外部链接
- ext2-doc project: Second Extended File System -面向实现的文档,用自然语言描述的内部结构。
- Design and Implementation of the Second Extended Filesystem (overview)
- State of the Art: Where we are with the Ext3 filesystem - Mingming Cao, Theodore Y. Ts'o, Badari Pulavarty和Suparna Bhattacharya的论文描述了ext2的扩展功能