ext2磁盘布局分析

加载一个磁盘,格式化为ext2,并mount到mnt,创建一个test目录和 test0,test1,test2子目录。在test子目录中创建一个带有hole的文件。

1.1加载一个磁盘及工具分析

本文采用 linux loop设备作为虚拟磁盘,具体参考如下链接:

http://www.cnblogs.com/linuxcat/archive/2012/08/12/2634424.html

  1. 生成一个全0的本地文件:

$ dd if=/dev/zero of=~/file.img bs=1M count=40

  1. 将上步中的文件生成一个loop设备:

$ sudo losetup –find –show ~/file.img

/dev/loop0

  1. 将设备格式化磁盘为ext2:

$ sudo mkfs -t ext2 /dev/loop0

 

格式化完成后会输出的文件系统的关键信息,具体如下:

mke2fs 1.42.9 (4-Feb-2014)

Discarding device blocks: done                           

Filesystem label=

OS type: Linux

Block size=1024 (log=0)

Fragment size=1024 (log=0)

Stride=0 blocks, Stripe width=0 blocks

10240 inodes, 40960 blocks

2048 blocks (5.00%) reserved for the super user

First data block=1

Maximum filesystem blocks=41943040

5 block groups

8192 blocks per group, 8192 fragments per group

2048 inodes per group

Superblock backups stored on blocks:

       8193, 24577

 

Allocating group tables: done                           

Writing inode tables: done                           

Writing superblocks and filesystem accounting information: done

  1. 挂载mount到mnt

$ sudo mount /dev/loop0 /mnt/zsn_ext2

$ ls /mnt

lost+found

  1. 创建目录

$ cd /mnt

$ sudo mkdir test

$ cd test

$ sudo mkdir test0 test1 test2

  1. 获得 ext2的信息
  2.  

$ sudo dumpe2fs /dev/loop0

or

$ dumpe2fs ~/file.img

 

dumpe2fs 1.42.9 (4-Feb-2014)

Filesystem volume name:   <none>

Last mounted on:          <not available>

Filesystem UUID:          889f2ce4-4221-4f87-ba9b-a1c771f1b595

Filesystem magic number:  0xEF53

Filesystem revision #:    1 (dynamic)

Filesystem features:      ext_attr resize_inode dir_index filetype sparse_super

Filesystem flags:         signed_directory_hash

Default mount options:    user_xattr acl

Filesystem state:         not clean

Errors behavior:          Continue

Filesystem OS type:       Linux

Inode count:              10240

Block count:              40960

Reserved block count:     2048

Free blocks:              39172

Free inodes:              10229

First block:              1

Block size:               1024

Fragment size:            1024

Reserved GDT blocks:      159

Blocks per group:         8192

Fragments per group:      8192

Inodes per group:         2048

Inode blocks per group:   256

Filesystem created:       Mon May 22 17:53:02 2017

Last mount time:          Mon May 22 17:54:24 2017

Last write time:          Mon May 22 17:54:24 2017

Mount count:              1

Maximum mount count:      -1

Last checked:             Mon May 22 17:53:02 2017

Check interval:           0 (<none>)

Reserved blocks uid:      0 (user root)

Reserved blocks gid:      0 (group root)

First inode:              11

Inode size:                  128

Default directory hash:   half_md4

Directory Hash Seed:      23c8f2d0-fbdf-412f-bcfc-9f891cd76505

 

 

Group 0: (Blocks 1-8192)

  Primary superblock at 1, Group descriptors at 2-2

  Reserved GDT blocks at 3-161

  Block bitmap at 162 (+161), Inode bitmap at 163

 (+162)

  Inode table at 164-419 (+163)

  7759 free blocks, 2037 free inodes, 2 directories

  Free blocks: 434-8192

  Free inodes: 12-2048

Group 1: (Blocks 8193-16384)

  Backup superblock at 8193, Group descriptors at 8194-8194

  Reserved GDT blocks at 8195-8353

  Block bitmap at 8354 (+161), Inode bitmap at 8355 (+162)

  Inode table at 8356-8611 (+163)

  7773 free blocks, 2048 free inodes, 0 directories

  Free blocks: 8612-16384

  Free inodes: 2049-4096

Group 2: (Blocks 16385-24576)

  Block bitmap at 16385 (+0), Inode bitmap at 16386 (+1)

  Inode table at 16387-16642 (+2)

  7930 free blocks, 2044 free inodes, 4 directories

  Free blocks: 16647-24576

  Free inodes: 4101-6144

Group 3: (Blocks 24577-32768)

  Backup superblock at 24577, Group descriptors at 24578-24578

  Reserved GDT blocks at 24579-24737

  Block bitmap at 24738 (+161), Inode bitmap at 24739 (+162)

  Inode table at 24740-24995 (+163)

  7773 free blocks, 2048 free inodes, 0 directories

  Free blocks: 24996-32768

  Free inodes: 6145-8192

Group 4: (Blocks 32769-40959)

  Block bitmap at 32769 (+0), Inode bitmap at 32770 (+1)

  Inode table at 32771-33026 (+2)

  7933 free blocks, 2048 free inodes, 0 directories

  Free blocks: 33027-40959

  Free inodes: 8193-10240

 

  1. 创建hole的文件

#  sudo echo -n "hello world" | dd of=./hole bs=1024 seek=1

1.2分析磁盘

1.2.1概况

任何Ext2分区中的第一个块从不受Ext2文件系统的管理,因为这一块是为分区的引导扇区所保留的。Ext2分区的其余部分被分割成块组(block group),每个块组的分布图如下图1所示。正如你从图中所看到的,一些数据结构正好可以放在一块中,而另一些可能需要更多的块。在Ext2文件系统中的所有块组大小相同并被顺序存放,因此,内核可以从块组的整数索引很容易地得到磁盘中一个块组的位置。

ext2_disk_p

由于内核尽可能地把属于同一个文件的数据块存放在同一块组中,所以块组可有效地提高文件连续性。每个块组均包含如下方面的信息:

  1. 文件系统超级块的拷贝;
  2. 文件系统所有块组描述符;
  3. 块组数据块位图;
  4. inode表位图;
  5. inode表;
  6. 块组内的数据块

事实上,只有块组0中所包含超级块和组描述符才由内核使用,而其余的超级块和组描述符都保持不变,内核甚至不考虑它们。当e2fsck程序对Ext2文件系统的状态执行一致性检查时,就引用存放在块组0中的超级块和组描述符,然后将它们拷贝到其他所有的块组中。如果出现数据损坏,并且块组0中的主超级块和主描述符变为无效,那么,系统管理员就可以命令e2fsck引用存放在某个块组(除了第一个块组)中的超级块和组描述符的旧拷贝。通常情况下,这些多余的拷贝所存放的信息足以让e2fsck把Ext2分区带回到一个一致的状态。

 

1.2.2超级块

在上一小节我们了解了ext2的大体结构,知道文件系统会被划分为若干个块组,而每个块组又包含超级块、组描述符、块位图和inode位图等信息。超级块结构表示一个文件系统。它包含管理文件系统所需的信息,包括文件系统名称(比如 ext2)、文件系统的大小和状态、块设备的引用和元数据信息(比如空闲列表等等)。在上节中我们通过dumpe2fs命令,已经获取到超级块的一些核心信息,下面我们分析一下ext2相关数据结构及磁盘分布情况,如图3.2为磁盘分布情况和linux内核中数据结构的对应关系。

ext2_disk_sb

在上述对应关系中,以inode数量为例,我们已经知道有10240个inode节点。而我们根据3.2.1中的描述,已经知道超级块位于第二个块开始的位置。可以用ue打开file.img文件,定位到1k的位置(也即400),同时对比源代码数据结构中s_inodes_count,可以看出s_inodes_count的值为00 28 00 00,转化对齐方式则为00 00 28 00,转成10进制则为10240,由此可以看出磁盘数据与数据结构的对应关系。

 

1.2.3块组

超级块后面的数据是组描述符,描述了块组的基本信息。以通过dumpe2fs工具获得的块组0为例,其信息如下:

Group 0: (Blocks 1-8192)

  Primary superblock at 1, Group descriptors at 2-2

  Reserved GDT blocks at 3-161

  Block bitmap at 162 (+161), Inode bitmap at 163 (+162)

  Inode table at 164-419 (+163)

  7759 free blocks, 2037 free inodes, 2 directories

  Free blocks: 434-8192

  Free inodes: 12-2048

通过上述信息可以看到超级块的位置、inode位图的位置和inode表的位置等信息。而我们对比看一下linux内核的数据结构,可以看到如下内容:

 

Group 2: (Blocks 16385-24576)

  Block bitmap at 16385 (+0), Inode bitmap at 16386 (+1)

  Inode table at 16387-16642 (+2)

  7930 free blocks, 2044 free inodes, 4 directories

  Free blocks: 16647-24576

  Free inodes: 4101-6144

对于块位图在28800位置(a2*400),定位到该位置,可以看到被使用的块都标记为1,而未被使用的则为0.

 

对于inode位图与此类似,这里不再赘述。

 

http://images.cnitblog.com/i/86842/201408/040038007122121.jpg

 

 

 

第2个组块中的inode信息。

$ sudo dd if=~/file.img bs=1 count=1024 skip=16387K | od -t x1 -Ax
000000 ed 41 00 00 00 04 00 00 95 fd dd 53 94 fd dd 53
000010 94 fd dd 53 00 00 00 00 00 00 05 00 04 00 00 00
000020 00 00 00 00 04 00 00 00 04 41 00 00 00 00 00 00                 #  block = 16644, topa 目录
000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000060 00 00 00 00 6f db 63 e9 03 41 00 00 00 00 00 00
000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1024+0 records in
1024+0 records out
000080 ed 41 00 00 00 04 00 00 94 fd dd 53 94 fd dd 53
1024 bytes (1.0 kB) copied

000090 94 fd dd 53 00 00 00 00 00 00 02 00 04 00 00 00
0000a0 00 00 00 00 01 00 00 00 05 41 00 00 00 00 00 00              #  block = 16645, suba 目录
, 0.00127417 s, 804 kB/s
0000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000e0 00 00 00 00 70 db 63 e9 03 41 00 00 00 00 00 00
0000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000100 ed 41 00 00 00 04 00 00 94 fd dd 53 94 fd dd 53
000110 94 fd dd 53 00 00 00 00 00 00 02 00 04 00 00 00
000120 00 00 00 00 01 00 00 00 06 41 00 00 00 00 00 00
000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000160 00 00 00 00 71 db 63 e9 03 41 00 00 00 00 00 00
000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000180 ed 41 00 00 00 04 00 00 94 fd dd 53 94 fd dd 53
000190 94 fd dd 53 00 00 00 00 00 00 02 00 04 00 00 00
0001a0 00 00 00 00 01 00 00 00 07 41 00 00 00 00 00 00
0001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0001e0 00 00 00 00 72 db 63 e9 03 41 00 00 00 00 00 00
0001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000400

分析目录

$ sudo dd if=~/file.img bs=1024 count=1 skip=420 | hexdump -C
1+0 records in
1+0 records out
00000000  02 00 00 00 0c 00 01 02  2e 00 00 00 02 00 00 00  |…………….|
1024 bytes (1.0 kB) copied00000010  0c 00 02 02 2e 2e 00 00  0b 00 00 00 14 00 0a 02  |…………….|
, 8.4743e-05 s, 12.1 MB/s
00000020  6c 6f 73 74 2b 66 6f 75  6e 64 00 00 01 10 00 00  |lost+found……|
00000030  d4 03 04 02 74 6f 70 61  00 00 00 00 00 00 00 00  |….topa……..|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |…………….|
*
00000400
 

$ sudo dd if=~/file.img bs=1024 count=1 skip=16644 | hexdump -C
1+0 records in
1+0 records out
00000000  01 10 00 00 0c 00 01 02  2e 00 00 00 02 00 00 00  |…………….|               # inode = 4097, topa,  inode = 2, 根目录
00000010  0c 00 02 02 2e 2e 00 00  02 10 00 00 0c 00 04 02  |…………….|
00000020  73 75 62 61 03 10 00 00  0c 00 04 02 73 75 62 62  |suba……..subb|
00000030  04 10 00 00 d0 03 04 02  73 75 62 63 00 00 00 00  |……..subc….|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |…………….|
 

当前目录:

内核源代码中fs/ext2/ext2.h 文件:

struct ext2_dir_entry_2 {
        __le32  inode;          /* Inode number */              # 01 10 00 00 = 4097
        __le16  rec_len;        /* Directory entry length */       # 0c 00 = 12
        __u8    name_len;        /* Name length */                 # 01 = 1
        __u8    file_type;                                                      # 02, 目录
        char    name[];            /* File name, up to EXT2_NAME_LEN */
};
 

$ ll -ia topa/
total 9
4097 drwxr-xr-x. 5 root root 1024 Aug  3 17:15 .
   2 drwxr-xr-x. 4 root root 1024 Aug  3 17:14 ..
4098 drwxr-xr-x. 2 root root 1024 Aug  3 17:15 suba
4099 drwxr-xr-x. 2 root root 1024 Aug  3 17:15 subb
4100 drwxr-xr-x. 2 root root 1024 Aug  3 17:15 subc

分析文件:

之前还没有创建文件,现在创建一个hole文件。

#  sudo echo -n "hello world" | dd of=./hole bs=1024 seek=1

途中,有6个block是hole。 本例子中只有一个block是hole。

http://images.cnitblog.com/i/86842/201408/040050024308643.jpg

$ sudo dd if=~/file.img bs=1 count=1024 skip=16387K | od -t x1 -Ax 

# 偏移量在 128 * 200 的位置。

000200 a4 81 00 00 0b 04 00 00 56 5d de 53 56 5d de 53
000210 56 5d de 53 00 00 00 00 00 00 01 00 04 00 00 00
000220 00 00 00 00 01 00 00 00 00 00 00 00 01 42 00 00      // 00 00 00 00 为hole, 00 00 00 01 为hello world
000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

$ sudo dd if=~/file.img bs=1024 count=1 skip=16897 | hexdump -C

1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied00000000  68 65 6c 6c 6f 20 77 6f  72 6c 64 00 00 00 00 00  |hello world…..|
, 5.7942e-05 s, 17.7 MB/s
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |…………….|
*
00000400

 

1.2.4inode、文件与数据

前文我们易经理了解到磁盘的空间首先被划分为块组,然后块组中又包含超级块、块组描述符、位图和inode表等内容。要了解文件系统的原理,首先要理解inode(详细信息请参考这里)。

什么是inode?

我们知道文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。具体包含:

  * 文件的字节数

  * 文件拥有者的User ID

  * 文件的Group ID

  * 文件的读、写、执行权限

  * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。

  * 链接数,即有多少文件名指向这个inode

  * 文件数据block的位置

我们可以通过stat命令了解文件/目录信息,也即inode信息。以上文中建立的test目录为例,执行stat命令可以得到如下信息:

  File: ‘test’

  Size: 1024        Blocks: 2          IO Block: 1024   directory

Device: 700h/1792d   Inode: 4097        Links: 5

Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)

Access: 2017-05-24 16:52:17.000000000 +0800

Modify: 2017-05-25 09:04:50.000000000 +0800

Change: 2017-05-25 09:04:50.000000000 +0800

 Birth: –

 

磁盘布局

我们已经对inode的基本概念有了一些了解。本节将重点分析一下inode在磁盘的分布情况,也即我们在操作系统看到的文件夹和文件在磁盘的分布情况。我们首先想到的是对于磁盘上的数据已经确定,操作系统是如何识别层级关系和其上的数据的。

我们知道通常的操作流程是将磁盘设备挂载到某个目录下面,此时就可以在该目录下访问磁盘中的内容。由此我们可以知道磁盘上数据可以访问的起点在挂载的过程中建立,关于挂载的细节我们放到后续章节解释,本节不重点关心,但我们知道这是挂载的过程中操作系统从磁盘的某个位置读取数据,并建立文件逻辑关系。

一个简单的方法,我们可以通过stat命令查看该设备的根目录的信息,从返回的信息可以看到数据位于inode为2的区域(如图3.2所示)。同时,我们在ext2的头文件中找到关于根的inode的位置的宏定义(如图3.3所示)。通过这两个信息我们可以知道,对于ext2文件系统,其根目录的数据总是存储在inode索引为2的位置。

 

图3.3 ext2文件系统宏定义

 

由前文我们已经知道inode表的位置,又知道inode的大小是128字节,因此可以知道inode 2的位置29080,由此可以知道存储该inode的数据的位置,具体如图3.4所示。在inode中包含的元数据信息都可以从磁盘的位置找到,如方框所标注的,包括连接时间,创建时间和数据块的位置等信息。

如图所示,inode的数据数据的位置由数据结构中的i_block[EXT2_N_BLOCKS]域确定,蓝色方框是inode数据的具体位置,由其值(01a4)可以知道数据看的具体位置为69000.在该数据块中存储着该文件夹下所有文件和子文件的信息。

 

另外需要重点介绍一下i_block,其用于存储文件的数据块位置信息。我们知道如果一个文件有多个数据块,这些数据块很可能不是连续存放的,应该如何寻址到每个块呢?根据上面的分析,根目录的数据块是通过其inode中的索引项Blocks[0]找到的,事实上,这样的索引项一共有15个,从Blocks[0]到Blocks[14],每个索引项占4字节。前12个索引项都表示块编号,例如上面的例子中Blocks[0]字段保存着0x01a4,就表示第0x01a4个块是该文件的数据块,如果块大小是1KB,这样可以表示从0字节到12KB的文件。如果剩下的三个索引项Blocks[12]到Blocks[14]也是这么用的,就只能表示最大15KB的文件了,这是远远不够的,事实上,剩下的三个索引项都是间接索引。

索引项Blocks[12]所指向的块并非数据块,而是称为间接寻址块(Indirect Block),其中存放的都是类似Blocks[0]这种索引项,再由索引项指向数据块。设块大小是b,那么一个间接寻址块中可以存放b/4个索引项,指向b/4个数据块。所以如果把Blocks[0]到Blocks[12]都用上,最多可以表示b/4+12个数据块,对于块大小是1K的情况,最大可表示268K的文件。如下图所示,注意文件的数据块编号是从0开始的,Blocks[0]指向第0个数据块,Blocks[11]指向第11个数据块,Blocks[12]所指向的间接寻址块的第一个索引项指向第12个数据块,依此类推。具体如图所示。

数据块的寻址

本文针对位于69000的数据块进行分析。由上图我们知道根文件夹有4个子文件夹,分别是本文件夹、父文件夹、lost+found和test。我们以test子文件夹为例,通过文件夹数据结构可以看到名称在最后,以此为基础向前4个字节既是该文件夹的inode的索引值。具体如图所示。

 

 

 

以此类推,我们根据该inode的值进一步得到test子文件夹下的内容。

文件

本节对一个4MB+的文件进行分析,了解其数据在磁盘的分布情况。

前文已经知道test子文件夹的inode编号是4097,根据前面的块组描述信息可以知道该inode具体位置。具体过程不再详细分析,本文直接定位到存储该文件夹的数据区域,如图所示:

这里有所有文件的名称、类型及inode信息。以test.log为例,可以知道其inode为0X00001008,也即4104,与通过stat命令获得的信息一致。然后本文定位到该inode的位置,具体内容如图所示,其中红色区域是前12个指针,后面蓝色区域内是间接索引。

以第一个索引为例,我们可以找到数据块的具体位置,内容如图所示,由图中内容可以看到正是我们写入的内容。

我们已经知道inode i_block域的前12个指针指向直接的块,也就是只能存储12KB的数据(以本例),因此需要用到间接寻址。定位到间接寻址的位置(0x4202*400=0x 1080800),可以看到其中的数据,其中存储的是数据块的索引,可以看到与之前的是连续的。

如果引用本站的原创文章,请注明原文链接:,本站保留追究责任的权利!

发表评论