Ext2

来自osdev
跳到导航 跳到搜索
文件系统
虚拟文件系统

VFS

磁盘文件系统
CD/DVD 文件系统
Network 文件系统
Flash 文件系统

Second Extended Filesystemext2fs)是对原始“扩展文件系统(Extended Filesystem)”的重写,因此也基于“inodes”的概念从20世纪90年代初到21世纪初,Ext2作为Linux事实上的文件系统存在了近十年,目前它被日志文件系统Ext3ReiserFS取代。 它具有对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类型和权限

类型指示器占据这个16位字段的顶部十六进制数字 (位15到12)
类型值

十六进制

类型说明
0x1000 FIFO
0x2000 Character device
0x4000 Directory
0x6000 Block device
0x8000 Regular file
0xA000 Symbolic link
0xC000 Unix socket
权限占据这个16位字段的底部12位
权限

值 (十六进制)

权限

八进制值

权限描述
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
开头

Byte

结尾

Byte

大小

以字节为单位

字段描述
116 116 1 Fragment number
117 117 1 Fragment size
118 119 2 (reserved)
120 121 2 High 16 bits of 32-bit User ID
122 123 2 High 16 bits of 32-bit Group ID
124 127 4 (reserved)
HURD
开头

字节

结尾

Byte

大小

以字节为单位

字段说明
116 116 1 Fragment number
117 117 1 Fragment size
118 119 2 High 16 bits of 32-bit "Type and Permissions" field
120 121 2 High 16 bits of 32-bit User ID
122 123 2 High 16 bits of 32-bit Group ID
124 127 4 User ID of author (if == 0xFFFFFFFF, the normal User ID will be used)
MASIX
开头

Byte

结尾

Byte

Size

以字节为单位

字段描述
116 116 1 Fragment number
117 117 1 Fragment size
118 127 X (reserved)

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)

快速摘要

如何读取索引节点

  1. 读取超级块以查找每个块的大小、每个组的块数、每个组的Inodes数以及第一个组的起始Block(Block Group Descriptor Table)。
  2. 确定inode属于哪个块组。
  3. 读取与包含要查找的inode的块组相对应的块组描述符。
  4. 从Block Group Descriptor中提取Block Group的inode表的位置。
  5. 确定inode表中inode的索引。
  6. 索引inode table(考虑非标准inode大小)。

目录条目信息和文件内容位于inode指向的数据块内。

如何读取根目录

根目录的inode定义为始终为2。读取/解析inode 2的内容。

另见

外部链接

de:Ext2