Linux文件系统-EXT文件系统结构及基本原理

1. 磁盘布局及文件系统结构

一般磁盘经过分区后,由MBR + MBR GAP + 若干分区组成.
a. MBR+MBR GAP一般是2048bytes, 主要用于写入引导程序(如grub、LILO等),引导系统启动;
b. MBR固定是512字节,446(引导写入区域)+64(分区表)+2(固定55aa)

磁盘分区结构图

分区进行格式化后,在分区的开头会预留空间作为Boot sector(一般1024bytes),剩下的空间切成若干个块组,块组的构成见下图.

文件系统空间分布

块组各部分功能描述

超级块(Super block)
a. 超级块用于描述文件系统的基本信息,如起始位置、block和inode的数量及大小、文件系统支持的特性等.
b. 超级块对于文件系统是至关重要的,一般位于块组0的第一个block中,若干备份存在其他块组中,超级块损坏,会导致文件系统无法识别.
c. 超级块示例如下

Filesystem volume name:   <none>
Last mounted on:          /root
Filesystem UUID:          c55383bd-6336-4503-a8c8-a644f340bc4c
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Filesystem flags:         signed_directory_hash 
Default mount options:    user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              6430720
Block count:              25700608
Reserved block count:     1285029
Free blocks:              7497348
Free inodes:              5878745
First block:              0
Block size:               4096
Fragment size:            4096
Group descriptor size:    64
Reserved GDT blocks:      1018
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
Flex block group size:    16
Filesystem created:       Thu Feb 18 10:40:42 2021
Last mount time:          Wed Mar 23 19:12:09 2022
Last write time:          Wed Mar 23 19:12:09 2022
Mount count:              172
Maximum mount count:      -1
Last checked:             Mon Nov  8 14:04:12 2021
Check interval:           0 (<none>)
Lifetime writes:          3056 GB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:           256
Required extra isize:     32
Desired extra isize:      32
Journal inode:            8
First orphan inode:       1049286
Default directory hash:   half_md4
Directory Hash Seed:      11a0c299-a897-4d08-a36e-5d72df2a9581
Journal backup:           inode blocks
Checksum type:            crc32c
Checksum:                 0xe080d5c7
Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3
Journal size:             256M
Journal length:           65536
Journal sequence:         0x013ce92a
Journal start:            48115
Journal checksum type:    crc32c
Journal checksum:         0xf282bb35

块组描述表(GDT)
主要用于描述每个块组的起始位置,块组内超级块、块组描述表、inode表、inode位图、数据块位图、数据块等具体位置,示例如下.

Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
  Checksum 0x2777, unused inodes 2004
  主 superblock at 1, Group descriptors at 2-4
  保留的GDT块位于 5-260
  Block bitmap at 261 (+260), Inode bitmap at 277 (+276)
  Inode表位于 293-544 (+292)
  3854 free blocks, 2004 free inodes, 2 directories, 2004个未使用的inodes
  可用块数: 4339-8192
  可用inode数: 13-2016
Group 1: (Blocks 8193-16384) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x2c84, unused inodes 2016
  备份 superblock at 8193, Group descriptors at 8194-8196
  保留的GDT块位于 8197-8452
  Block bitmap at 262 (bg #0 + 261), Inode bitmap at 278 (bg #0 + 277)
  Inode表位于 545-796 (bg #0 + 544)
  7932 free blocks, 2016 free inodes, 0 directories, 2016个未使用的inodes
  可用块数: 8453-16384
  可用inode数: 2017-4032
Group 2: (Blocks 16385-24576) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x2336, unused inodes 2016
  Block bitmap at 263 (bg #0 + 262), Inode bitmap at 279 (bg #0 + 278)
  Inode表位于 797-1048 (bg #0 + 796)
  8192 free blocks, 2016 free inodes, 0 directories, 2016个未使用的inodes
  可用块数: 16385-24576
  可用inode数: 4033-6048
Group 3: (Blocks 24577-32768) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x5ca1, unused inodes 2016
  备份 superblock at 24577, Group descriptors at 24578-24580
  保留的GDT块位于 24581-24836
  Block bitmap at 264 (bg #0 + 263), Inode bitmap at 280 (bg #0 + 279)
  Inode表位于 1049-1300 (bg #0 + 1048)
  7932 free blocks, 2016 free inodes, 0 directories, 2016个未使用的inodes
  可用块数: 24837-32768
  可用inode数: 6049-8064

块位图(Block bitmap)
数据块使用情况对照表,1bit对应一个数据块.
inode位图(inode bitmap)
inode块使用情况对照表,1bit对应一个数据块.
inode table
inode是文件索引项,每个文件对应一个inode,inode中记录了文件的基本属性及关联的数据块,inode数据结构如下.

struct ext4_inode {
    __le16  i_mode;     /* File mode */
    __le16  i_uid;      /* Low 16 bits of Owner Uid */
    __le32  i_size_lo;  /* Size in bytes */
    __le32  i_atime;    /* Access time */
    __le32  i_ctime;    /* Inode Change time */
    __le32  i_mtime;    /* Modification time */
    __le32  i_dtime;    /* Deletion Time */
    __le16  i_gid;      /* Low 16 bits of Group Id */
    __le16  i_links_count;  /* Links count */
    __le32  i_blocks_lo;    /* Blocks count */
    __le32  i_flags;    /* File flags */
    union {
        struct {
            __le32  l_i_version;
        } linux1;
        struct {
            __u32  h_i_translator;
        } hurd1;
        struct {
            __u32  m_i_reserved1;
        } masix1;
    } osd1;             /* OS dependent 1 */
    __le32  i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
    __le32  i_generation;   /* File version (for NFS) */
    __le32  i_file_acl_lo;  /* File ACL */
    __le32  i_size_high;
    __le32  i_obso_faddr;   /* Obsoleted fragment address */
    union {
        struct {
            __le16  l_i_blocks_high; /* were l_i_reserved1 */
            __le16  l_i_file_acl_high;
            __le16  l_i_uid_high;   /* these 2 fields */
            __le16  l_i_gid_high;   /* were reserved2[0] */
            __le16  l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
            __le16  l_i_reserved;
        } linux2;
        struct {
            __le16  h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
            __u16   h_i_mode_high;
            __u16   h_i_uid_high;
            __u16   h_i_gid_high;
            __u32   h_i_author;
        } hurd2;
        struct {
            __le16  h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
            __le16  m_i_file_acl_high;
            __u32   m_i_reserved2[2];
        } masix2;
    } osd2;             /* OS dependent 2 */
    __le16  i_extra_isize;
    __le16  i_checksum_hi;  /* crc32c(uuid+inum+inode) BE */
    __le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */
    __le32  i_mtime_extra;  /* extra Modification time(nsec << 2 | epoch) */
    __le32  i_atime_extra;  /* extra Access time      (nsec << 2 | epoch) */
    __le32  i_crtime;       /* File Creation time */
    __le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
    __le32  i_version_hi;   /* high 32 bits for 64-bit version */
    __le32  i_projid;   /* Project ID */
};

数据块(data block)
用于存放数据的block,每个block固定大小和唯一编号.

文件系统运行基本原理

普通文件与目录区别
普通文件与目录,都是文件,都有唯一的inode作为指向索引及数据块记录其内容. 两者的区别主要在内容上,目录的数据块记录的是一张表,这张表描述目录下所有文件文件名和inode的一一对应关系.

文件创建过程(顺序不一定对)
a. 分配inode x和block y给新文件,x指向y
b. 更新inode和block位图
c. 在新文件父目录的数据块中,更新文件名-inode对照表,增加一条记录

文件读取过程
文件读取,是从根目录开始,一层一层往下查找的,以/etc/fstab为例如下:
a. 读取/的inode(根目录的inode固定,一般为2),找到/的block,进行读取,找到etc的inode:
b. 读取etc的inode后,找到etc/的block,读取获得fstab的inode
c. 读取fstab的inode,获得fstab的block
d. 再读取fstab的block,即获取fstab内容

文件系统结构

2. ext3/4 JDB日志系统介绍

JDB日志系统是ext3在ext2的基础上增加的功能.

2.1 JDB日志系统功能阐述

假设系统运行在ext2分区上,正在读写磁盘. 突然掉电,或系统崩溃,你不得不强制重启系统,然而此时还有数据在内存缓冲区未写入磁盘;重启系统后,你可能会发现部分数据丢失,甚至文件系统元数据丢失,文件系统不完整一致,分区无法挂载等;调用fsck可能会修复分区,但会耗费大量时间. JDB日志系统主要是为应对此类情况而设计的,但JDB不能减少系统崩溃的概率,它主要解决的问题是:a. 系统出现异常重启时,尽量保持文件系统的完整和一致性(完整和一致性指文件系统元数据如超级块、块组描述表、块位图、inode位图、inode表及数据块,互相之间的对照关系是准确的,比如块位图和数据块实际被占用的情况一致,inode表和数据块的映射关系是准确的等);b. 文件系统损坏后,可修复的情况下,减少修复的耗时,主要是利用日志记录进行修复.

2.2 JDB运行基本原理

a. 定义原子操作
修改文件系统的任一系统调用都通常划分为操纵磁盘数据结构的一系列低级操作. 原子操作是单个低级操作或若干低级操作的组合,是内部不再分割的操作,该操作要么完全完成,要么根本没有执行,不存在部分完成的状态。比如说为文件分配一个磁盘块,可以看成一个原子操作。分配一个磁盘块,可能需要修改一个inode块、一个磁盘块位图、最多三个间接索引块、块组块、超级块,一共最多7个磁盘块。将分配一个磁盘块看成一个原子操作,意味着上述修改7个磁盘块的操作要么都成功,要么都失败,不可能有第三种状态。

b. 一系列原子操作组合成一个事务
实现日志文件系统时,可以将一个原子操作就作为一个事务来处理,但是这样实现的效率比较低。若干个原子操作组合成一个事务,对磁盘日志以事务为单位进行管理,以提高读写日志的效率.

c. 在磁盘上划分空间存储事务日志
将原子操作组成的事务,写到日志空间上,这部分日志即为磁盘数据操作的历史记录,利用这部分数据回溯,可实现数据还原.

JDB数据写入原理图

d. 通过事务状态跟踪事务完成情况
事务运行会经历下面的一系列状态:
运行(running):事务当前在内存中,还可以接受新的原子操作。在一个系统中,仅有一个事务可以处于运行状态
锁定(locked):事务不再接受新的原子操作,但现有原子操作们还没有完成。一旦所有原子操作都完成了,事务将进入下一个状态
写入(flush):事务中的所有原子操作都完成了,事务正在写入日志
提交(commit):事务已写入日志。事务会写一个提交块,指示事务log已写入日志
完成(Finished):事务写到日志之后,它会留在那直到所有的块都被更新到磁盘上的实际位置

2.3 JDB事务日志结构

JDB事务日志结构图

从上图可以看到,JDB日志有超级块、描述块、数据块、提交块及取消块组成.
a. 超级块(JFS_SUPERBLOCK):日志中超级块起的作用与文件系统中超级块的作用是类似的,都是用于组织管理一段磁盘空间.
b. 描述块(JFS_DESCRIPTOR_BLOCK):一个事务以描述块开始,以提交块结束. 描述块主要作用是描述本事务中的日志块,记录的是哪个磁盘块的操作记录.
c. 数据块:记录磁盘块的数据操作
d. 提交块(JFS_COMMIT_BLOCK):提交块表明一个事务的完成.
e. 取消块(JFS_REVOKE_BLOCK):事务中包含删除磁盘块操作时,就会在日志中写一个取消块,表明取消块之前,对应磁盘块的操作都可以忽略.

通过debugfs查看JDB日志

debugfs:  logdump -a
Journal starts at block 1, transaction 51
Found expected sequence 51, type 1 (descriptor block) at block 1
Dumping descriptor block, sequence 51, at block 1:
  FS block 293 logged at journal block 2 (flags 0x0)
  FS block 277 logged at journal block 3 (flags 0x2)
  FS block 2 logged at journal block 4 (flags 0x2)
  FS block 294 logged at journal block 5 (flags 0x2)
  FS block 4325 logged at journal block 6 (flags 0x2)
  FS block 1 logged at journal block 7 (flags 0xa)
Found expected sequence 51, type 2 (commit block) at block 8
Found expected sequence 52, type 1 (descriptor block) at block 9
Dumping descriptor block, sequence 52, at block 9:
  FS block 294 logged at journal block 10 (flags 0x8)
Found expected sequence 52, type 2 (commit block) at block 11
Found expected sequence 53, type 1 (descriptor block) at block 12
Dumping descriptor block, sequence 53, at block 12:
  FS block 277 logged at journal block 13 (flags 0x0)
  FS block 2 logged at journal block 14 (flags 0x2)
  FS block 294 logged at journal block 15 (flags 0x2)
  FS block 293 logged at journal block 16 (flags 0x2)
  FS block 4325 logged at journal block 17 (flags 0x2)
  FS block 262 logged at journal block 18 (flags 0xa)
Found expected sequence 53, type 2 (commit block) at block 19
Found expected sequence 54, type 1 (descriptor block) at block 20
Dumping descriptor block, sequence 54, at block 20:
  FS block 294 logged at journal block 21 (flags 0x0)
  FS block 4325 logged at journal block 22 (flags 0x2)
  FS block 293 logged at journal block 23 (flags 0x2)
  FS block 1 logged at journal block 24 (flags 0x2)
  FS block 2 logged at journal block 25 (flags 0x2)
  FS block 277 logged at journal block 26 (flags 0x2)
  FS block 131105 logged at journal block 27 (flags 0xa)
Found expected sequence 54, type 2 (commit block) at block 28
Found expected sequence 55, type 1 (descriptor block) at block 29
Dumping descriptor block, sequence 55, at block 29:
  FS block 135137 logged at journal block 30 (flags 0x0)
  FS block 131105 logged at journal block 31 (flags 0x2)
  FS block 1 logged at journal block 32 (flags 0x2)
  FS block 131075 logged at journal block 33 (flags 0x2)
  FS block 3 logged at journal block 34 (flags 0x2)
  FS block 131089 logged at journal block 35 (flags 0xa)
Found expected sequence 55, type 2 (commit block) at block 36
Found expected sequence 56, type 1 (descriptor block) at block 37
Dumping descriptor block, sequence 56, at block 37:
  FS block 131105 logged at journal block 38 (flags 0x0)
  FS block 293 logged at journal block 39 (flags 0x2)
  FS block 131089 logged at journal block 40 (flags 0x2)
  FS block 3 logged at journal block 41 (flags 0x2)
  FS block 135138 logged at journal block 42 (flags 0x2)
  FS block 1 logged at journal block 43 (flags 0xa)
Found expected sequence 56, type 2 (commit block) at block 44
Found expected sequence 57, type 1 (descriptor block) at block 45
Dumping descriptor block, sequence 57, at block 45:
  FS block 131105 logged at journal block 46 (flags 0x0)
  FS block 131089 logged at journal block 47 (flags 0x2)
  FS block 3 logged at journal block 48 (flags 0x2)
  FS block 135138 logged at journal block 49 (flags 0xa)
Found expected sequence 57, type 2 (commit block) at block 50
Found expected sequence 58, type 1 (descriptor block) at block 51
Dumping descriptor block, sequence 58, at block 51:
  FS block 262 logged at journal block 52 (flags 0x0)
  FS block 2 logged at journal block 53 (flags 0x2)
  FS block 131105 logged at journal block 54 (flags 0xa)
Found expected sequence 58, type 2 (commit block) at block 55
Found expected sequence 59, type 1 (descriptor block) at block 56
Dumping descriptor block, sequence 59, at block 56:
  FS block 131105 logged at journal block 57 (flags 0x0)
  FS block 135138 logged at journal block 58 (flags 0x2)
  FS block 1 logged at journal block 59 (flags 0x2)
  FS block 3 logged at journal block 60 (flags 0x2)
  FS block 131089 logged at journal block 61 (flags 0xa)
Found expected sequence 59, type 2 (commit block) at block 62
Found sequence 36 (not 60) at block 63: end of journal.

3. ext2/3/4文件系统调试工具介绍

mke2fs:用于创建ext文件系统
dumpe2fs:查看文件系统超级块和块组描述表
tune2fs:用于调整文件系统参数
e2fsck:检查和修复文件系统
debugfs:文件系统debug工具,功能强大,可以用来查看JDB日志
extundelete:利用JDB日志,修复被删除的文件
badblocks:检查磁道坏块

4. 关于文件系统&数据的恢复的思考

a. 对于异常被删除的数据,第一时间卸载磁盘,避免新的数据覆盖旧数据,然后通过对JDB日志分析,有可能能恢复数据,extundelete工具就是利用的这个原理.
b. 文件系统损坏的话,可尝试通过fsck、e2fsck工具进行修复,fsck会全盘检查,效率较低;e2fsck利用JDB日志,修复效率会更高.
c. 文件系统结构破坏的情况下,是否有机会恢复数据?
这种情况是最让人头疼的,也是最复杂的,仅从理论上分析,应该是有可能的,主要考虑以下几个方面的问题:
i:文件系统的目录结构存在于inode table和数据块中,如果能定位到这2部分的位置,应该就能恢复大部分数据.
ii:关于inode table的位置定位,inode块通常是连续且大小固定的,inode又是统一固定的数据结构,猜测通过特征比对,应该能识别出来,定位到连续的inode块,也就能定位到inode位置.
iii:数据块的起始位置就在inode table的结束位置.
iiii:还有就是要确定数据块的编号,数据块的编号定位不准确的话,获取的内容错误的,并且会导致连锁反应的错误,所以必须要精确.

5. 参考文献

Ext3文件系统及JDB介绍
journal block device jbd源代码分析

6. 遗留

文件系统结构被破坏,文件系统和数据的恢复手段

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容