Linux文件系统分析

时间:2021-11-11 08:00:10

这里的分析有别于一般书上讲的Linux文件系统分析,主要区别是:这里的分析与其说是文件系统分析倒不如说是Linux磁盘映象的分析,

首先从获取磁盘映象开始,然后分析磁盘映象的结构,内核文件系统部分的代码无非就是对磁盘映象的管理罢了。
    依据读源代码的体会,Linux文件系统实现可以看作三个部分:用户眼中的文件系统,内存中的文件系统,磁盘上的文件系统。用户眼中

的文件系统是指open,read等系统调用的实现以及目录树状结构的实现,内存中的文件系统是指缓冲管理,VFS等开机mount后在内存中出现的

文件系统,而磁盘上的文件系统就是指磁盘映象。这里的分析主要着重在第三部分,即磁盘映象。
    首先就是磁盘映象的获取,最简单的方法就是使用dd命令:dd if=/dev/hda of=/home/lw/hda.dd bs=2k。这个命令把hda上的文件系统磁

盘映象全部复制到hda.dd文件中,这个命令执行需要的时间主要取决于hda磁盘的大小,bs参数表示每次读些的单元大小,一般设为2k,8k都

行,效率上会有些细微的区别。接下来就对hda.dd文件进行分析就可以了。由于这个文件一般都很大,二进制阅读工具运行起来会很慢,所以

最好还是建立一个软盘ext2文件系统,然后进行分析。当然还有其他的方法:直接拷贝虚拟机磁盘映象来分析。
    下面的分析是使用软盘上建立的文件系统,然后dd倒出来的rootimage文件来分析磁盘映象。让我们先来看看第一个扇区的内容
00000000h: EB 3C 90 4D 53 44 4F 53 35 2E 30 00 02 01 01 00 ; ?怣SDOS5.0.....
00000010h: 02 E0 00 40 0B F0 09 00 12 00 02 00 00 00 00 00 ; .?@.?.........
00000020h: 00 00 00 00 00 00 29 57 23 8D 0E 20 20 20 20 20 ; ......)W#?    
00000030h: 20 20 20 20 20 20 46 41 54 31 32 20 20 20 FA 33 ;       FAT12   ?
00000040h: C0 8E D0 BC 00 7C 16 07 BB 78 00 36 C5 37 1E 56 ; 缼屑.|..粁.6?.V
00000050h: 16 53 BF 3E 7C B9 0B 00 FC F3 A4 06 1F C6 45 FE ; .S?|?.?.艵?

00000060h: 0F 8B 0E 18 7C 88 4D F9 89 47 02 C7 07 3E 7C FB ; .?.|圡鶋G.?>|?

00000070h: CD 13 72 79 33 C0 39 06 13 7C 74 08 8B 0E 13 7C ; ?ry3?..|t.?.|
00000080h: 89 0E 20 7C A0 10 7C F7 26 16 7C 03 06 1C 7C 13 ; ? |?|?.|...|.
00000090h: 16 1E 7C 03 06 0E 7C 83 D2 00 A3 50 7C 89 16 52 ; ..|...|円.|?R
000000a0h: 7C A3 49 7C 89 16 4B 7C B8 20 00 F7 26 11 7C 8B ; ||?K|?.?.|?

000000b0h: 1E 0B 7C 03 C3 48 F7 F3 01 06 49 7C 83 16 4B 7C ; ..|.肏黧..I|?K|
000000c0h: 00 BB 00 05 8B 16 52 7C A1 50 7C E8 92 00 72 1D ; .?.?R||钂.r.
000000d0h: B0 01 E8 AC 00 72 16 8B FB B9 0B 00 BE E6 7D F3 ; ?璎.r.孄?.炬}?

000000e0h: A6 75 0A 8D 7F 20 B9 0B 00 F3 A6 74 18 BE 9E 7D ; .? ?.螃t.緸}
000000f0h: E8 5F 00 33 C0 CD 16 5E 1F 8F 04 8F 44 02 CD 19 ; 鑏.3劳.^.?廌.?
00000100h: 58 58 58 EB E8 8B 47 1A 48 48 8A 1E 0D 7C 32 FF ; XXX腓婫.HH?.|2?BR>00000110h: F7 E3 03 06 49 7C 13 16 4B 7C BB 00 07 B9 03 00 ; 縻..I|..K|?.?.
00000120h: 50 52 51 E8 3A 00 72 D8 B0 01 E8 54 00 59 5A 58 ; PRQ?.r匕.鑄.YZX
00000130h: 72 BB 05 01 00 83 D2 00 03 1E 0B 7C E2 E2 8A 2E ; r?..円....|忖?
00000140h: 15 7C 8A 16 24 7C 8B 1E 49 7C A1 4B 7C EA 00 00 ; .|?$|?I||?.
00000150h: 70 00 AC 0A C0 74 29 B4 0E BB 07 00 CD 10 EB F2 ; p.?纓)??.?腧
00000160h: 3B 16 18 7C 73 19 F7 36 18 7C FE C2 88 16 4F 7C ; ;..|s.?.|?O|
00000170h: 33 D2 F7 36 1A 7C 88 16 25 7C A3 4D 7C F8 C3 F9 ; 3吟6.|?%||?

00000180h: C3 B4 02 8B 16 4D 7C B1 06 D2 E6 0A 36 4F 7C 8B ; 么.?M|?益.6O|?

00000190h: CA 86 E9 8A 16 24 7C 8A 36 25 7C CD 13 C3 0D 0A ; 蕟閵.$|?%|??.
000001a0h: 4E 6F 6E 2D 53 79 73 74 65 6D 20 64 69 73 6B 20 ; Non-System disk
000001b0h: 6F 72 20 64 69 73 6B 20 65 72 72 6F 72 0D 0A 52 ; or disk error..R
000001c0h: 65 70 6C 61 63 65 20 61 6E 64 20 70 72 65 73 73 ; eplace and press
000001d0h: 20 61 6E 79 20 6B 65 79 20 77 68 65 6E 20 72 65 ;  any key when re
000001e0h: 61 64 79 0D 0A 00 49 4F 20 20 20 20 20 20 53 59 ; ady...IO      SY
000001f0h: 53 4D 53 44 4F 53 20 20 20 53 59 53 00 00 55 AA ; SMSDOS   SYS..U
这个块称为引导块,注意最后的55AA就是许多书上讲Linux启动时所谓的引导标志了,没有其它的内容可以分析的了。
接下来,我将对这个磁盘映象一个扇区一个扇区地分析了。

  第二个扇区和第一个扇区一样属于引导块,这里就不列举出其内容了,这里的一块是两个扇区即1024B。接下来的一块就是大名鼎鼎的超级块了。其内容如下:
00000400h: E0 01 A0 05 01 00 01 00 13 00 00 00 00 1C 08 10 ; ??............
00000410h: 7F 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ...............
00000420h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000430h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000440h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000450h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000460h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
后面一直到该磁盘块结束的地方全部都是0:
000007f0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
    下面列出这个超级块的结构:
struct super_block{
 unsigned short s_ninodes; i节点数(1E0=480)
 unsigned short s_nzones; 逻辑块数(5A0=1440刚好是软盘的容量)
 unsigned short s_imap_blocks; i节点位图所占用的数据块数(1块)
 unsigned short s_zmap_blocks; 逻辑块位图所占用的数据块数(1块)
 unsigned short s_firstdatazone; 第一个数据逻辑块号(13H=19块)
 unsigned short s_log_zone_size; log(数据块数/逻辑块)
 unsigned long  s_max_size; 文件最大长度
 unsigned short s_magic;  文件系统魔数(137F)
...//后面的成员仅出现在内存中,不列举出来。
    这个superblock的信息就这么多,它表示该文件系统有480个i节点,逻辑块数是1440,每块是1024B,所以总容量就是软盘容量了。i节点位图和逻辑块位图都只占用一个数据块。第一个数据块块号是19号,这里存放的是"/"根文件了,等会儿就可以看到了。
    接下来的一个磁盘块就是所谓的i节点位图块了,这已经是第3个磁盘块了。
00000800h: FF FF FF FF FF FF DD FF 7F 01 00 00 00 00 00 00 ; ???????.......
00000810h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000820h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000830h: 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF ; ............????
00000840h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; ????????????????
00000850h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; ????????????????
00000860h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; ????????????????
后面一直到该磁盘块结束的地方全部都是1:
00000bf0h: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; ????????????????
    这里到00000830h行0结束的地方,总共有60个字节,即480位,与超级块中的i节点数480吻合。这480个位中,为1的表示i节点在用,为0的表示空闲。
    逻辑块位图的原理和i节点位图一模一样,这里省略其分析。到此已经分析完了4个磁盘块。
  i节点即index node索引节点。接下来的是第五个磁盘块了,480个i节点,每个i节点占用32个字节,总共占用480*32/1024=15个磁盘块,现在再重新理清一下磁盘块的关系了:
0块:引导块,每块1024B
1块:超级块
2块:i节点位图
3块:逻辑块位图
4...18:i节点区块
19块:数据块,刚好和Linux文件系统分析之二中提到的数据块开始于第19块吻合。
    下面分析第一个i节点,它位于块号为4的块中,即是第五块了。
00001000h: ED 41 00 00 80 00 00 00 9E AA 2E 84 00 08 13 00 ; 鞟..?..灙.?...
00001010h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
为分析这二进制中的数据涵义,下面列出i节点结构,这个i节点其实就是表示根目录了。
struct d_inode{
 unsigned short i_mode;
 unsigned short i_uid;
 unsigned long i_size;
 unsigned long i_time;
 unsigned char i_gid;
 unsigned char i_nlinks;
 unsigned short i_zone[9];
}
刚好是32个字节,
i_mode表示文件类型和属性,这里是41ED即0100.0001.1110.1101
见stat.h文件中的定义:
第一个四位0100表示这是一个目录文件,确实如此,这就是根目录。
接下来的三位000表示文件的属性,执行或搜索时是否设置uid
最后面的九位1.1110.1101表示rwxrwxrwx了,这个用过linux的都知道

i_uid为0000,表示这个文件的所有者的uid是0,通常root用户的uid就是0

i_size为00000080,表示这个文件的长度为80H=128个字节,后面可以验证。

i_time为842EAA9E,表示这个文件的创建时间

i_gid为00,表示该文件所有者所在group的id为0

i_nlinks为08,表示有8个文件目录项指向该i节点

最后一个13H即19表示该文件的数据块号,后面都是0,表示该文件在一个数据块中存得下,不用更多的数据块。

分析之四将追溯到这个i节点的数据块即19块,用以说明目录项的数据结构。
下面顺着分析之三的思路,追溯到19号磁盘块,首先算出其偏移地址19*1024=19456即4C00处,这里的磁盘映象如下:
00004c00h: 01 00 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00004c10h: 01 00 2E 2E 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00004c20h: 02 00 62 69 6E 00 00 00 00 00 00 00 00 00 00 00 ; ..bin...........
00004c30h: 06 00 64 65 76 00 00 00 00 00 00 00 00 00 00 00 ; ..dev...........
00004c40h: 19 00 65 74 63 00 00 00 00 00 00 00 00 00 00 00 ; ..etc...........
00004c50h: 1E 00 75 73 72 00 00 00 00 00 00 00 00 00 00 00 ; ..usr...........
00004c60h: 3C 00 74 6D 70 00 00 00 00 00 00 00 00 00 00 00 ; <.tmp...........
00004c70h: 44 00 72 6F 6F 74 00 00 00 00 00 00 00 00 00 00 ; D.root..........
00004c80h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 该块后面都为0

这里每一行,即16个字节,表示一个目录项。
struct dir_entry{
 unsigned short inode;
 char name[NAME_LEN];
};
第一个目录项,4c00那一行中,inode为01,表示该目录所在i节点为第1个i节点,name为"."就是所谓的当前目录了"/",这个目录i节点确实

就是第一个,即我们在分析之三中分析的那个。
第二个目录项,inode为01,name为".."表示上一层目录,目前已是根目录了,上层目录也是根目录,这就是为什么在根目录时执行cd ..命令

时结果还是根目录。
第三个目录项,inode为02,name为"bin",它就表示/bin目录了。
后面的几个同理可知其涵义。如此,这个文件系统根目录下面有.,..,bin,dev,etc,usr,tmp,root这8个目录了,每个目录占用16个字

节,总共就占用了128个字节了,这个数字和分析之三中的i_size为128刚好吻合。

下面我们顺藤摸瓜,看看这个root目录下面有些什么东西。root位于第44H=68个i节点,在磁盘映象中找到如下:
00001860h: ED 41 00 00 20 00 00 00 00 58 9C 29 00 02 4D 05 ; 鞟.. ....X?..M.
00001870h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
这个root目录的i节点显示其数据块在054DH=1357块中,即1357*1024=153400H处
00153400h: 44 00 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 ; D...............
00153410h: 01 00 2E 2E 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
除了两个.,..目录外这个目录下面什么也没有,可以看到.目录就是当前目录,其i节点为44,..指向根目录,而根目录i节点为01,和前面的

分析刚好吻合。
再看看usr目录下面有什么东西,usr位于第1EH=30个i节点,再磁盘映象中找到如下:
000013a0h: ED 41 00 00 50 00 00 00 80 79 73 29 00 05 95 01 ; 鞟..P...?ys)..?
000013b0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
这个目录的i节点显示其数据块在0195H=405块中,即405*1024=414720=65400H处
00065400h: 1E 00 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00065410h: 01 00 2E 2E 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00065420h: 1F 00 62 69 6E 00 00 00 00 00 00 00 00 00 00 00 ; ..bin...........
00065430h: 3B 00 6C 6F 63 61 6C 00 00 00 00 00 00 00 00 00 ; ;.local.........
00065440h: 3D 00 72 6F 6F 74 00 00 00 00 00 00 00 00 00 00 ; =.root..........
显然比root目录多了bin,local,root目录了,下面往该文件系统中写一个文件,看看通过这里的分析方法是否能够找到。

通过rootimage引导,往/usr/root目录下面写入hello.c文件,在分析之四中已经到达了/usr/root目录,在进一步看看这个目录里面有什么内容:
    这个目录位于第3DH=61个i节点,再磁盘映象中找到如下:
00001780h: FF 41 00 00 50 00 00 00 48 A2 47 40 00 02 29 05 ; ?A..P...H@..).
00001790h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
所以/usr/root目录的实际数据存放在0529H=1321块中,1321*1024=1352704=14A400H偏移地址处
0014a400h: 3D 00 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 ; =...............
0014a410h: 1E 00 2E 2E 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
0014a420h: 3E 00 2E 62 61 73 68 5F 68 69 73 74 6F 72 79 00 ; >..bash_history.
0014a430h: 42 00 61 2E 6F 75 74 00 00 00 00 00 00 00 00 00 ; B.a.out.........
0014a440h: 45 00 68 65 6C 6C 6F 2E 63 00 00 00 00 00 00 00 ; E.hello.c.......
0014a450h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
还是老一套,同样有.,..目录,除此之外还有.bash_history,a.out,hello.c文件,就是我们写进去的文件了。该文件的i节点号为45H=69,找到此i节点:
00001880h: 80 81 00 00 4A 00 00 00 48 A2 47 40 00 01 9A 05 ; ??.J...H@..?
00001890h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
这个i节点就是hello.c文件的i节点,这个文件的数据放在059AH=1434块中,1434*1024=1468416=166800H偏移地址处
00166800h: 23 69 6E 63 6C 75 64 65 20 3C 73 74 64 69 6F 2E ; #include <stdio.
00166810h: 68 3E 0A 0A 69 6E 74 20 6D 61 69 6E 28 29 0A 7B ; h>..int main().{
00166820h: 0A 09 70 72 69 6E 74 66 28 22 48 65 6C 6C 6F 2C ; ..printf("Hello,
00166830h: 20 77 6F 72 6C 64 21 5C 6E 22 29 3B 0A 09 72 65 ;  world!/n");..re
00166840h: 74 75 72 6E 20 30 3B 0A 7D 0A 00 00 00 00 00 00 ; turn 0;.}.......
00166850h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00166860h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00166870h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
这个hello.c就这样显身了。
在详细地看看这个i节点的内容,
i_mode是8180即1000.0001.1000.0000
第一个四位1000表示这是一个常规文件,接下来的三位000表示文件的属性,执行或搜索时是否设置uid
最后面的九位1.1000.0000表示rwxrwxrwx了,这是个文件主人可以读写,但是不能执行的文件,其它组或人都不能读写执行该文件。

i_uid为0000,表示这个文件的所有者是root

i_size为0000004A,表示这个文件的长度为74个字节,数一数,确实是74个字节。

i_nlinks为01,表示有1个文件目录项指向该i节点,可以创建链接ln再来看看这个是否为1。

 

转自:http://lwccb.blog.163.com/blog/static/116970122005112994010/