読者です 読者をやめる 読者になる 読者になる

ext4:ディスクレイアウト調査中めも1

linux ext4

何周遅れだよ!って気もするけどext4のディスク上のレイアウトを調べる。現状調べたのはファイルのinodeにたどり着くとこまでで、ファイルのデータにアクセスするところまではできてない。。。

基本的にはext4wikiにあるExt4_Disk_Layoutのページを参考に。 テスト用に以下のようにディスクイメージを作り、1ファイルほど置いておく。

$ dd if=/dev/zero of=ext4.img bs=1M count=1024
$ mkfs.ext4 ./ext4.img

ファイルの内容はこんな感じで。

#!/bin/bash

for i in {1..16}
do
    echo "abcdefgh" >> test.txt
done

そうしたらhexdumpでダンプを採ってあとはdebugfsコマンドや、自分でコードを書いたりしながら資料と見比べる。 まずは最初にディスクレイアウトの基本から。レイアウトの説明はwikiLayoutにて下記のように書かれているだけど、実際にはこの通りにならない。

Group 0 Padding ext4 Super Block Group Descriptors Reserved GDT Blocks Data Block Bitmap inode Bitmap inode Table Data Blocks
1024 bytes 1 block many blocks many blocks 1 block 1 block many blocks many more blocks

Flexible Block Groups(flex_bg)という機能がこれが有効になっている場合はデータの置き方が変わってブロックグループ0にブロックグループ0以外のブロックグループのinode bitmapやらdata block bitmapなどを置くようにするけど、この置き方が連続して置かれない場合もある模様。そうすると上記図のような置き方にならない。 ここで先ほど作ったディスクイメージをdebugfsコマンドで見てみる。コマンドラインdebugfs -R stats ext4.img

 Group  0: block bitmap at 65, inode bitmap at 81, inode table at 97
           28585 free blocks, 8180 free inodes, 2 used directories, 8180 unused inodes
           [Checksum 0xc776]
 Group  1: block bitmap at 66, inode bitmap at 82, inode table at 609
           32702 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0x4b25]
 Group  2: block bitmap at 67, inode bitmap at 83, inode table at 1121
           32768 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Block not init, Checksum 0xfe76]
 Group  3: block bitmap at 68, inode bitmap at 84, inode table at 1633
           32703 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0x8923]
 Group  4: block bitmap at 69, inode bitmap at 85, inode table at 2145
           24576 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0xcd19]
 Group  5: block bitmap at 70, inode bitmap at 86, inode table at 2657
           32703 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0xb41e]
 Group  6: block bitmap at 71, inode bitmap at 87, inode table at 3169
           32768 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Block not init, Checksum 0xfd1c]
 Group  7: block bitmap at 72, inode bitmap at 88, inode table at 3681
           32703 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0xf58d]

このコマンドの出力にある数値はすべて10進数。で、ブロックグループは8個あってブロックグループ0のblock bitmapは65(0x41)ブロック目、ブロックグループ1は66(0x42)、以下略という感じになっている。 そうすると、ブロックグループ7のblock bitmapの終端からブロックグループ1のinode bitmapの開始位置まで空きがあるのがわかり、inode tableも同様なことがわかる。 ということで、ディスク上のレイアウトは図と多少違うということを念頭に進めていく必要がある。。。

では、まずはディスクの先頭部分から。ディスクの先頭は当然ブロックグループ0になっているけど、この中でGroup 0 Paddingの1024バイトの領域は使われない。これは単純な話でディスクの先頭はブートセクタとして利用されている可能性もあるのでそこは飛ばさないといけいないというのが理由。 そして、スーパーブロックはディスクの先頭1024バイト目から始まる。スーパーブロックが1024から始まるのはブロックが1Kでも2K、4Kどの場合でも無駄になる領域が減らせられるからでしょう。ext4のスーパーブロックのサイズは1024バイト。よってブロック0の先頭1024バイトはファイルシステムとしては未使用、1024バイト目から1KiB分がスーバーブロックで残りは未使用。 この辺りのダンプはこのようなもの。

00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|   <-- block group0
*
00000400  00 00 01 00 00 00 04 00  33 33 00 00 a4 ce 03 00  |........33......|   <-- super block
00000410  f4 ff 00 00 00 00 00 00  02 00 00 00 02 00 00 00  |................|
00000420  00 80 00 00 00 80 00 00  00 20 00 00 2f fe e4 52  |......... ../..R|
00000430  44 fe e4 52 01 00 ff ff  53 ef 01 00 01 00 00 00  |D..R....S.......|
00000440  15 fe e4 52 00 00 00 00  00 00 00 00 01 00 00 00  |...R............|
00000450  00 00 00 00 0b 00 00 00  00 01 00 00 3c 00 00 00  |............<...|
00000460  42 02 00 00 7b 00 00 00  b2 79 38 06 17 ac 41 3e  |B...{....y8...A>|
00000470  93 eb 4e 00 66 73 8d aa  00 00 00 00 00 00 00 00  |..N.fs..........|
00000480  00 00 00 00 00 00 00 00  2f 6d 6e 74 00 00 00 00  |......../mnt....|
00000490  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000004c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 3f 00  |..............?.|
000004d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000004e0  08 00 00 00 00 00 00 00  00 00 00 00 2e 56 47 ee  |.............VG.|
000004f0  f1 1a 42 cd 84 6b b6 f1  0a 83 e6 65 01 01 00 00  |..B..k.....e....|
00000500  0c 00 00 00 00 00 00 00  15 fe e4 52 0a f3 01 00  |...........R....|
00000510  04 00 00 00 00 00 00 00  00 00 00 00 00 20 00 00  |............. ..|
00000520  00 00 02 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000530  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000540  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 02  |................|
00000550  00 00 00 00 00 00 00 00  00 00 00 00 1c 00 1c 00  |................|
00000560  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000570  00 00 00 00 04 00 00 00  81 81 00 00 00 00 00 00  |................|
00000580  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

ext4のデータはリトルエンディアンでジャーナル機能のjbd2が使用している部分はビッグエンディアンらしい。何でエンディアンを混ぜるか>< この領域がext4のスーパーブロックかどうかをチェックするには0x438-0x439の2バイトを見る。ここが0xEF53ならext4のスーパーブロック。struct ext4_inodeだとs_magicがその部分。 struct ext4_super_blockの定義はここ。

スーパーブロックの次はGroup Descriptors。これは1ブロックに収まるとは限らない。これの開始位置は1ブロック目から。 ここにはBlock Group Descriptorsが置かれていて、データのサイズが64バイトもしくは32バイト。この大きさはスーパーブロックのs_feature_incompatにあるINCOMPAT_64BIT次第。このビットが立っていたら64バイト、そうでなければ32bit。 INCOMPAT_64BITは64bitのアドレッシング(実際は48bitのようだけど)が使えるようになるのでファイルシステムが最大1EiBand、ファイルサイズが最大で16TiB使える。 ext4ではこのような構造体

285 struct ext4_group_desc
286 {
287         __le32  bg_block_bitmap_lo;     /* Blocks bitmap block */
288         __le32  bg_inode_bitmap_lo;     /* Inodes bitmap block */
289         __le32  bg_inode_table_lo;      /* Inodes table block */
290         __le16  bg_free_blocks_count_lo;/* Free blocks count */
291         __le16  bg_free_inodes_count_lo;/* Free inodes count */
292         __le16  bg_used_dirs_count_lo;  /* Directories count */
293         __le16  bg_flags;               /* EXT4_BG_flags (INODE_UNINIT, etc) */
294         __le32  bg_exclude_bitmap_lo;   /* Exclude bitmap for snapshots */
295         __le16  bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bbitmap) LE */
296         __le16  bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+ibitmap) LE */
297         __le16  bg_itable_unused_lo;    /* Unused inodes count */
298         __le16  bg_checksum;            /* crc16(sb_uuid+group+desc) */
299         __le32  bg_block_bitmap_hi;     /* Blocks bitmap block MSB */
300         __le32  bg_inode_bitmap_hi;     /* Inodes bitmap block MSB */
301         __le32  bg_inode_table_hi;      /* Inodes table block MSB */
302         __le16  bg_free_blocks_count_hi;/* Free blocks count MSB */
303         __le16  bg_free_inodes_count_hi;/* Free inodes count MSB */
304         __le16  bg_used_dirs_count_hi;  /* Directories count MSB */
305         __le16  bg_itable_unused_hi;    /* Unused inodes count MSB */
306         __le32  bg_exclude_bitmap_hi;   /* Exclude bitmap block MSB */
307         __le16  bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bbitmap) BE */
308         __le16  bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+ibitmap) BE */
309         __u32   bg_reserved;
310 };

ext3のグループディスクリプタはこのような構造体。

102 struct ext3_group_desc
103 {
104         __le32  bg_block_bitmap;                /* Blocks bitmap block */
105         __le32  bg_inode_bitmap;                /* Inodes bitmap block */
106         __le32  bg_inode_table;         /* Inodes table block */
107         __le16  bg_free_blocks_count;   /* Free blocks count */
108         __le16  bg_free_inodes_count;   /* Free inodes count */
109         __le16  bg_used_dirs_count;     /* Directories count */
110         __u16   bg_pad;
111         __le32  bg_reserved[3];
112 };

グループディスクリプタに限らないけどext3からext4の拡張でext3の頃に使っていたアドレスとかの大きさが増えている。 例えば、ext3のbg_block_bitmapはext4ではbg_block_bitmap_loとして、ブロックビットマップのアドレスの下位32bitとして扱い、上位32ビットはext3では使っていなかったオフセットにあるという感じで。 さて、INCOMPAT_64BITが立っていない場合、ディスクリプタのサイズは32バイトなのでext3_group_descを使っても値は取得できるんだけど、ext4の拡張部分に当たるbg_flagsを扱うにはext4のグループディスクリプタとして見たほうが楽というのもある。bg_flagsはext3のbg_padとbg_reservedを使っているので。

Group Descriptorsは1ブロックとは限らないので何ブロック使っているかは別途計算が必要。 この場合、最初にブロックグループの数を計算する。これはスーパーブロックのs_blocks_count_loとs_blocks_count_hiを64bit整数にしてs_blocks_per_groupで割ればOK。 次に((ブロックグループ数 * ディスクリプタ-のサイズ) / ブロックサイズ) + 1で完了。 cで書くとこんな感じで。

#define make_integer(type, lo, hi) ({\
       typeof(type) bitmask = (typeof(type)) -1; \
       typeof(type) tmp = bitmask & hi; \
       u8 bit_shift = sizeof(typeof(type)) * 8; \
       tmp << bit_shift | (lo & bitmask); })


static inline u64
u32_to_u64(u32 lo, u32 hi)
{
    return make_integer(u64, lo, hi);
}

static void
show_ext4_sb_data(const struct ext4_super_block *sb)
{
~~~
        nr_group_desc = u32_to_u64(sb->s_blocks_count_lo, sb->s_blocks_count_hi) / sb->s_blocks_per_group;
    printf("[-]Number of Block groups: %d\n", nr_group_desc);
    putchar('\n');
    
    printf("[-]Flex BG\n");
    printf("s_log_groups_per_flex: 0x%x : 0x%x\n", sb->s_log_groups_per_flex, 2 << sb->s_log_groups_per_flex);
    putchar('\n');

    nr_group_desc_blocks = ((nr_group_desc * 
                                ((sb->s_feature_incompat & 0x80) ? sizeof(struct ext4_group_desc) : sizeof(struct ext3_group_desc))) / block_size) + 1;
    printf("Group descriptors use %d blocks\n", nr_group_desc_blocks);
~~~

今使っているディスクイメージの場合、s_blocks_count_loとs_blocks_count_hi、s_blocks_per_groupはこのような値。ブロックサイズは4KiB。

s_blocks_count_lo: 0x40000
s_blocks_count_hi: 0x0
s_blocks_per_group: 0x8000

よって、ブロックグループ数は8。

masami@saga:~/codes/read_ext4$ echo "obase=16;ibase=16; 40000/8000" | bc
8

必要なブロック数は1。

masami@saga:~/codes/read_ext4$ echo "obase=16;ibase=16; ((8 * 32) / 1000) + 1" | bc
1

ということでまたダンプを見てみる。

00001000  41 00 00 00 51 00 00 00  61 00 00 00 a9 6f f4 1f  |A...Q...a....o..|   <-- group0 descriptors
00001010  02 00 04 00 00 00 00 00  00 00 00 00 f4 1f 76 c7  |..............v.|
00001020  42 00 00 00 52 00 00 00  61 02 00 00 be 7f 00 20  |B...R...a...... |
00001030  00 00 05 00 00 00 00 00  00 00 00 00 00 20 25 4b  |............. %K|
00001040  43 00 00 00 53 00 00 00  61 04 00 00 00 80 00 20  |C...S...a...... |
00001050  00 00 07 00 00 00 00 00  00 00 00 00 00 20 76 fe  |............. v.|
00001060  44 00 00 00 54 00 00 00  61 06 00 00 bf 7f 00 20  |D...T...a...... |
00001070  00 00 05 00 00 00 00 00  00 00 00 00 00 20 23 89  |............. #.|
00001080  45 00 00 00 55 00 00 00  61 08 00 00 00 60 00 20  |E...U...a....`. |
00001090  00 00 05 00 00 00 00 00  00 00 00 00 00 20 19 cd  |............. ..|
000010a0  46 00 00 00 56 00 00 00  61 0a 00 00 bf 7f 00 20  |F...V...a...... |
000010b0  00 00 05 00 00 00 00 00  00 00 00 00 00 20 1e b4  |............. ..|
000010c0  47 00 00 00 57 00 00 00  61 0c 00 00 00 80 00 20  |G...W...a...... |
000010d0  00 00 07 00 00 00 00 00  00 00 00 00 00 20 1c fd  |............. ..|
000010e0  48 00 00 00 58 00 00 00  61 0e 00 00 bf 7f 00 20  |H...X...a...... |
000010f0  00 00 05 00 00 00 00 00  00 00 00 00 00 20 8d f5  |............. ..|
00001100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

このディスクは32バイトのグループディスクリプタを使っている。最初64バイトだと思っていて計算が合わなかったけど、INCOMPAT_64BITのことに気づいて納得した。 ここの内容は上のほうにも貼ったやつだけど、このような内容。

 Group  0: block bitmap at 65, inode bitmap at 81, inode table at 97
           28585 free blocks, 8180 free inodes, 2 used directories, 8180 unused inodes
           [Checksum 0xc776]
 Group  1: block bitmap at 66, inode bitmap at 82, inode table at 609
           32702 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0x4b25]
 Group  2: block bitmap at 67, inode bitmap at 83, inode table at 1121
           32768 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Block not init, Checksum 0xfe76]
 Group  3: block bitmap at 68, inode bitmap at 84, inode table at 1633
           32703 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0x8923]
 Group  4: block bitmap at 69, inode bitmap at 85, inode table at 2145
           24576 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0xcd19]
 Group  5: block bitmap at 70, inode bitmap at 86, inode table at 2657
           32703 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0xb41e]
 Group  6: block bitmap at 71, inode bitmap at 87, inode table at 3169
           32768 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Block not init, Checksum 0xfd1c]
 Group  7: block bitmap at 72, inode bitmap at 88, inode table at 3681
           32703 free blocks, 8192 free inodes, 0 used directories, 8192 unused inodes
           [Inode not init, Checksum 0xf58d]

ちなみに、debugfsの出力だとInode not init、Block not initが出ているけど実際にはEXT4_BG_INODE_ZEROEDというフラグもある。 自分で書いたコードで見てみると各グループは以下のようなビットが立っていた。EXT4_BG_BLOCK_UNINITの立っているグループ2とグループ6は全部0で埋められているのでダンプだと省略されていた。

Group0
EXT4_BG_INODE_ZEROED

Group1
EXT4_BG_INODE_UNINIT
EXT4_BG_INODE_ZEROED

Group2
EXT4_BG_INODE_UNINIT
EXT4_BG_BLOCK_UNINIT
EXT4_BG_INODE_ZEROED

Group3
EXT4_BG_INODE_UNINIT
EXT4_BG_INODE_ZEROED

Group4
EXT4_BG_INODE_UNINIT
EXT4_BG_INODE_ZEROED

Group5
EXT4_BG_INODE_UNINIT
EXT4_BG_INODE_ZEROED

Group6
EXT4_BG_INODE_UNINIT
EXT4_BG_BLOCK_UNINIT
EXT4_BG_INODE_ZEROED

Group7
EXT4_BG_INODE_UNINIT
EXT4_BG_INODE_ZEROED

Group Descriptorsの次に来るのはReserved GDT Blocks。これはグループディスクリプタの予約領域。 これの数はs_reserved_gdt_blocksを見れば良くて、debugfsでも当然確認可能。このディスクでは63になっている。ダンプはこのように。

00002000  02 80 00 00 02 80 01 00  02 80 02 00 02 80 03 00  |................|   <-- Reserved group descriptors
00002010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00003000  03 80 00 00 03 80 01 00  03 80 02 00 03 80 03 00  |................|  
00003010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00004000  04 80 00 00 04 80 01 00  04 80 02 00 04 80 03 00  |................|
00004010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
~~~
0003f000  3f 80 00 00 3f 80 01 00  3f 80 02 00 3f 80 03 00  |?...?...?...?...|
0003f010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00040000  40 80 00 00 40 80 01 00  40 80 02 00 40 80 03 00  |@...@...@...@...|
00040010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

次に来るのは何かというとグループディスクリプタでみたblock bitmap。ブロックグループ0は「block bitmap at 65」とdebugfsでも表示されている。 ブロック65ということは0x41(ブロック番号) * 0x1000(ブロックサイズ)して0x41000とわかる。ブロックグループ7はブロック72なので0x49000までがblock bitmapのデータ。

00041000  ff ff ff ff ff ff ff ff  ff 3f fe 01 fe ff ff ff  |.........?......|   <-- group0 block bitmap
00041010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00041200  ff ff ff ff ff ff ff ff  ff ff ff ff 03 00 00 00  |................|
00041210  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00042000  ff ff ff ff ff ff ff ff  03 00 00 00 00 00 00 00  |................|   <-- group1 block bitmap
00042010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00044000  ff ff ff ff ff ff ff ff  01 00 00 00 00 00 00 00  |................|   <-- group3 block bitmap
00044010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00045000  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|   <-- group4 block bitmap
*
00045400  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|   
*
00046000  ff ff ff ff ff ff ff ff  01 00 00 00 00 00 00 00  |................|   <-- group5 block bitmap
00046010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00048000  ff ff ff ff ff ff ff ff  01 00 00 00 00 00 00 00  |................|   <-- group7 block bitmap
00048010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

このダンプの中でグループ2とグループ6がないのは多分EXT4_BG_BLOCK_UNINITが立っていたため。

では次はinode bitmapを見てみる。これも先ほどと同じでグループディスクリプタにあるinode bitmapのインデックスからアドレスを出すだけ。

00051000  ff 0f 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|   <-- group0 inode bitmap
00051010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00051400  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00052000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|   <-- group1 - group7  inode bitmap
*
00061000  00 00 00 00 00 00 00 00  19 fe e4 52 19 fe e4 52  |...........R...R|   <-- group0 inode table(actualy group0 to group7)

もう見えているけどinode tableの開始位置も見えている。 ここまででブロックグループ0に関してはData Blocks以外は見た形。ビットマップなんかはブロックグループ0にあるので他はみなくても良いや。

ブロックグループ0のダンプを見ているとどこにディレクトリエントリーがあるかはわかるんだけど、なぜ0x49000から32MBの領域なのかは不明。 wikiページの説明によるとグループディスクリプタやらビットマップグループ以外の領域はデータブロックとして使えるようだけど。まあ変なところにあるなと。この辺の理屈はもうちょい調べないとイケナイな。

00049000  02 00 00 00 0c 00 01 02  2e 00 00 00 02 00 00 00  |................|   <--- group0 directory entry 
00049010  0c 00 02 02 2e 2e 00 00  0b 00 00 00 14 00 0a 02  |................|
00049020  6c 6f 73 74 2b 66 6f 75  6e 64 00 00 0c 00 00 00  |lost+found......|
00049030  d4 0f 08 01 74 65 73 74  2e 74 78 74 00 00 00 00  |....test.txt....|   <--- 0xfd is rec_len
00049040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0004a000  0b 00 00 00 0c 00 01 02  2e 00 00 00 02 00 00 00  |................|   <--- end of directory entry for test.txt
0004a010  f4 0f 02 02 2e 2e 00 00  00 00 00 00 00 00 00 00  |................|
0004a020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0004b000  00 00 00 00 00 10 00 00  00 00 00 00 00 00 00 00  |................|   <--- end of directory entory for .. at the address 0x4a0c
0004b010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0004c000  00 00 00 00 00 10 00 00  00 00 00 00 00 00 00 00  |................|
0004c010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0004d000  00 00 00 00 00 10 00 00  00 00 00 00 00 00 00 00  |................|
0004d010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00051000  ff 0f 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|   <-- group0 inode bitmap

ディレクトリエントリーはstruct ext4_dir_entry_2が使われる。サイズは可変長でファイル名は最大EXT4_NAME_LEN文字(255)。

1623 struct ext4_dir_entry_2 {
1624         __le32  inode;                  /* Inode number */
1625         __le16  rec_len;                /* Directory entry length */
1626         __u8    name_len;               /* Name length */
1627         __u8    file_type;
1628         char    name[EXT4_NAME_LEN];    /* File name */
1629 };

ディレクトリエントリーのレコード長(rec_len)は4バイトアラインになるように作られ、4バイト境界のサイズにならない場合は'\n0'がnameに追加される。その他の注意点としてディレクトリエントリーはブロックを跨がない、また、最後のレコードのrec_lenはブロックの最後までの長さになる。 どういうことかというと、上のダンプでは"."、".."、"lost+found"、"test.txt"の4つのエントリが1ブロック(0x49000〜0x49fff)にあり、各データはこのようになる。

name: .
inode: 0x2
recode length: 0xc
name length: 0x1
file type: 0x2

name: ..
inode: 0x2
recode length: 0xc
name length: 0x2
file type: 0x2

name: lost+found
inode: 0xb
recode length: 0x14
name length: 0xa
file type: 0x2

name: test.txt
inode: 0xc
recode length: 0xfd4
name length: 0x8
file type: 0x1

ここで最後のエントリはtest.txtで、これのrec_lenが0xfd4。これはエントリの開始位置が0x4902cでブロックの境界が0x4a000なので引き算して0xfd4ということ。 なので最後のエントリの場合は実際に必要なデータのサイズよりも大きい形になる。

とりあえずここまででディレクトリエントリからtest.txtのinodeがわかったのでこのファイルのinodeを見てみる。 inodeのデータ構造はstruct ext4_inodeで定義される。このinodeは他のファイルシステムなどと同様にディスク上のinodeとメモリ上のinodeではデータ構造が変わる。ソースの方にもちゃんと書いてあるけど。

635 /*
636  * Structure of an inode on the disk
637  */
638 struct ext4_inode {

inodeの大きさはスーパーブロックのs_inode_sizeで読むことができてext4では256バイト。 該当のファイルのinodeからブロックのアドレスを算出するのは以下の用に。 ((inodeサイズ * (inode番号 - 1) * ブロックサイズ )+ inode tableの開始位置

test.txtの場合、inodeのサイズは256バイト、inode番号は0xCなので1引いて-1とすると0xb00となり、これにinode tableの開始位置をブロック番号(0x61) * ブロックサイズ(0x1000)として0x61000を足すと0x61b00となるのでここを見てみる。

00061b00  a4 81 00 00 90 00 00 00  35 fe e4 52 35 fe e4 52  |........5..R5..R|
00061b10  35 fe e4 52 00 00 00 00  00 00 01 00 08 00 00 00  |5..R............|
00061b20  00 00 08 00 01 00 00 00  0a f3 01 00 04 00 00 00  |................|
00061b30  00 00 00 00 00 00 00 00  01 00 00 00 41 80 00 00  |............A...|
00061b40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00061b60  00 00 00 00 63 35 cb df  00 00 00 00 00 00 00 00  |....c5..........|
00061b70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00061b80  1c 00 00 00 30 b1 32 6e  30 b1 32 6e 30 b1 32 6e  |....0.2n0.2n0.2n|
00061b90  35 fe e4 52 30 b1 32 6e  00 00 00 00 00 00 00 00  |5..R0.2n........|
00061ba0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

これだと分かりづらいのでinodeを読むようなコードを書いて出力してみる。

i_mode: 0x81a4
i_uid: 0x0
i_size_lo: 0x90
i_blocks_lo: 0x8
i_flags: 0x80000
osd1: 0x1
i_block[0]: 0x1f30a
i_block[1]: 0x4
i_block[2]: 0x0
i_block[3]: 0x0
i_block[4]: 0x1
i_block[5]: 0x8041
i_block[6]: 0x0
i_block[7]: 0x0
i_block[8]: 0x0
i_block[9]: 0x0
i_block[10]: 0x0
i_block[11]: 0x0
i_block[12]: 0x0
i_block[13]: 0x0
i_block[14]: 0x0
atime: (null)
ctime: (null)
mtime: Sun Jan 26 21:23:17 2014

これでもイマイチ確認しづらいのでマウントしてlsしたものと比較。

masami@saga:~/codes/read_ext4$ ls -laih /mnt
total 28K
 2 drwxr-xr-x  3 root root 4.0K Jan 26 21:23 .
 2 drwxr-xr-x 21 root root 4.0K Nov  1 06:35 ..
11 drwx------  2 root root  16K Jan 26 21:22 lost+found
12 -rw-r--r--  1 root root  144 Jan 26 21:23 test.txt

サイズはi_size_loで0x90、lsの方は144バイトと出ていてOK。mtimeも秒はわからないけどあってるので間違いないはず。

あとはブロックグループ4にディレクトリエントリーがあるとか、

20005000  02 00 00 00 0c 00 01 02  2e 00 00 00 02 00 00 00  |................|   <-- block grou4 directory entry
20005010  0c 00 02 02 2e 2e 00 00  0b 00 00 00 14 00 0a 02  |................|
20005020  6c 6f 73 74 2b 66 6f 75  6e 64 00 00 0c 00 00 00  |lost+found......|
20005030  d4 0f 08 01 74 65 73 74  2e 74 78 74 00 00 00 00  |....test.txt....|
20005040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

同じくブロックグループ4にtest.txtのinodeがあるとか、

2000eb00  a4 81 00 00 90 00 00 00  35 fe e4 52 35 fe e4 52  |........5..R5..R|
2000eb10  35 fe e4 52 00 00 00 00  00 00 01 00 08 00 00 00  |5..R............|
2000eb20  00 00 08 00 01 00 00 00  0a f3 01 00 04 00 00 00  |................|
2000eb30  00 00 00 00 00 00 00 00  01 00 00 00 41 80 00 00  |............A...|
2000eb40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
2000eb60  00 00 00 00 63 35 cb df  00 00 00 00 00 00 00 00  |....c5..........|
2000eb70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
2000eb80  1c 00 00 00 30 b1 32 6e  30 b1 32 6e 30 b1 32 6e  |....0.2n0.2n0.2n|
2000eb90  35 fe e4 52 30 b1 32 6e  00 00 00 00 00 00 00 00  |5..R0.2n........|
2000eba0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

ブロックグループ1のデータブロックにtest.txtのデータがある理由は調べる必要がある。

08041000  61 62 63 64 65 66 67 68  0a 61 62 63 64 65 66 67  |abcdefgh.abcdefg|   <-- data of test.txt
08041010  68 0a 61 62 63 64 65 66  67 68 0a 61 62 63 64 65  |h.abcdefgh.abcde|
08041020  66 67 68 0a 61 62 63 64  65 66 67 68 0a 61 62 63  |fgh.abcdefgh.abc|
08041030  64 65 66 67 68 0a 61 62  63 64 65 66 67 68 0a 61  |defgh.abcdefgh.a|
08041040  62 63 64 65 66 67 68 0a  61 62 63 64 65 66 67 68  |bcdefgh.abcdefgh|
08041050  0a 61 62 63 64 65 66 67  68 0a 61 62 63 64 65 66  |.abcdefgh.abcdef|
08041060  67 68 0a 61 62 63 64 65  66 67 68 0a 61 62 63 64  |gh.abcdefgh.abcd|
08041070  65 66 67 68 0a 61 62 63  64 65 66 67 68 0a 61 62  |efgh.abcdefgh.ab|
08041080  63 64 65 66 67 68 0a 61  62 63 64 65 66 67 68 0a  |cdefgh.abcdefgh.|
08041090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

debugfsの出力からinodeを使っているのはブロックグループ0だけだし。

( ´ー`)フゥー...

Practical File System Design

Practical File System Design