文章目录
前言
网上关于ext2文件系统的博客有很多,但看完之后还是有些云里雾里,于是我分析了一下数据结构和文件系统镜像二进制数据
【为了避免排版混乱,我引用了很多代码块标签,因为markdown会自动处理掉多的空格等符号】
ext2内容概述
第一部分 super block
第二部分 group_desc
第三部分 block_bitmap
第四部分 inode_bitmap
第五部分 inode_table
第六部分 dir entry
第七部分 data区
第八部分 间接块
第九部分 块组
实验准备
e2progfs源码下载地址
https://sourceforge.net/projects/e2fsprogs/
创建文件系统脚本
create_ext2img.sh
umount mnt
dd if=/dev/zero of=ext2.img bs=1024 count=128
mke2fs -t ext2 ext2.img -b 1024
hexdump -Cv ext2.img > 00.hex
创建几个制定大小文件脚本
make_files.sh
# 需要一个超多字符1的文件c1
# 只需改动bs即可改变目标文件大小
dd if=c1 of=file1 bs=16384 count=1
cp file1 file2
cp file1 file3
cp file1 file4
cp file1 file5
sed -i 's/1/2/g' file2
sed -i 's/1/3/g' file3
sed -i 's/1/4/g' file4
sed -i 's/1/5/g' file5
写入文件并导出2进制数据
mount ext2.img mnt
cp file1 mnt
cp file2 mnt
cp file3 mnt
cp file4 mnt
cp file5 mnt
umount mnt
hexdump -Cv ext2.img > f5.hex
二进制对比分析
以下是格式化完成之后 和按脚本操作写入文件之后的二进制对比
1 super block
超级块结构体定义
struct ext2_super_block {
/*000*/ __u32 s_inodes_count; /* Inodes count */
__u32 s_blocks_count; /* Blocks count */
__u32 s_r_blocks_count; /* Reserved blocks count */
__u32 s_free_blocks_count; /* Free blocks count */
/*010*/ __u32 s_free_inodes_count; /* Free inodes count */
__u32 s_first_data_block; /* First Data Block */
__u32 s_log_block_size; /* Block size */
__u32 s_log_cluster_size; /* Allocation cluster size */
/*020*/ __u32 s_blocks_per_group; /* # Blocks per group */
__u32 s_clusters_per_group; /* # Fragments per group */
__u32 s_inodes_per_group; /* # Inodes per group */
__u32 s_mtime; /* Mount time */
/*030*/ __u32 s_wtime; /* Write time */
__u16 s_mnt_count; /* Mount count */
__s16 s_max_mnt_count; /* Maximal mount count */
__u16 s_magic; /* Magic signature */
__u16 s_state; /* File system state */
__u16 s_errors; /* Behaviour when detecting errors */
__u16 s_minor_rev_level; /* minor revision level */
/*040*/ __u32 s_lastcheck; /* time of last check */
__u32 s_checkinterval; /* max. time between checks */
__u32 s_creator_os; /* OS */
__u32 s_rev_level; /* Revision level */
/*050*/ __u16 s_def_resuid; /* Default uid for reserved blocks */
__u16 s_def_resgid; /* Default gid for reserved blocks */
__u32 s_first_ino; /* First non-reserved inode */
__u16 s_inode_size; /* size of inode structure */
__u16 s_block_group_nr; /* block group # of this superblock */
__u32 s_feature_compat; /* compatible feature set */
/*060*/ __u32 s_feature_incompat; /* incompatible feature set */
__u32 s_feature_ro_compat; /* readonly-compatible feature set */
/*068*/ __u8 s_uuid[16] __nonstring; /* 128-bit uuid for volume */
/*078*/ __u8 s_volume_name[EXT2_LABEL_LEN] __nonstring; /* volume name, no NUL? */
/*088*/ __u8 s_last_mounted[64] __nonstring; /* directory last mounted on, no NUL? */
/*0c8*/ __u32 s_algorithm_usage_bitmap; /* For compression */
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
__u16 s_reserved_gdt_blocks; /* Per group table for online growth */
/*0d0*/ __u8 s_journal_uuid[16] __nonstring; /* uuid of journal superblock */
/*0e0*/ __u32 s_journal_inum; /* inode number of journal file */
__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
/*0ec*/ __u32 s_hash_seed[4]; /* HTREE hash seed */
/*0fc*/ __u8 s_def_hash_version; /* Default hash version to use */
__u8 s_jnl_backup_type; /* Default type of journal backup */
__u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */
/*100*/ __u32 s_default_mount_opts; /* default EXT2_MOUNT_* flags used */
__u32 s_first_meta_bg; /* First metablock group */
__u32 s_mkfs_time; /* When the filesystem was created */
/*10c*/ __u32 s_jnl_blocks[17]; /* Backup of the journal inode */
/*150*/ __u32 s_blocks_count_hi; /* Blocks count high 32bits */
__u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/
__u32 s_free_blocks_hi; /* Free blocks count */
__u16 s_min_extra_isize; /* All inodes have at least # bytes */
__u16 s_want_extra_isize; /* New inodes should reserve # bytes */
/*160*/ __u32 s_flags; /* Miscellaneous flags */
__u16 s_raid_stride; /* RAID stride in blocks */
__u16 s_mmp_update_interval; /* # seconds to wait in MMP checking */
__u64 s_mmp_block; /* Block for multi-mount protection */
/*170*/ __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
__u8 s_checksum_type; /* metadata checksum algorithm */
__u8 s_encryption_level; /* versioning level for encryption */
__u8 s_reserved_pad; /* Padding to next 32bits */
__u64 s_kbytes_written; /* nr of lifetime kilobytes written */
/*180*/ __u32 s_snapshot_inum; /* Inode number of active snapshot */
__u32 s_snapshot_id; /* sequential ID of active snapshot */
__u64 s_snapshot_r_blocks_count; /* active snapshot reserved blocks */
/*190*/ __u32 s_snapshot_list; /* inode number of disk snapshot list */
#define EXT4_S_ERR_START ext4_offsetof(struct ext2_super_block, s_error_count)
__u32 s_error_count; /* number of fs errors */
__u32 s_first_error_time; /* first time an error happened */
__u32 s_first_error_ino; /* inode involved in first error */
/*1a0*/ __u64 s_first_error_block; /* block involved in first error */
__u8 s_first_error_func[32] __nonstring; /* function where error hit, no NUL? */
/*1c8*/ __u32 s_first_error_line; /* line number where error happened */
__u32 s_last_error_time; /* most recent time of an error */
/*1d0*/ __u32 s_last_error_ino; /* inode involved in last error */
__u32 s_last_error_line; /* line number where error happened */
__u64 s_last_error_block; /* block involved of last error */
/*1e0*/ __u8 s_last_error_func[32] __nonstring; /* function where error hit, no NUL? */
#define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts)
/*200*/ __u8 s_mount_opts[64] __nonstring; /* default mount options, no NUL? */
/*240*/ __u32 s_usr_quota_inum; /* inode number of user quota file */
__u32 s_grp_quota_inum; /* inode number of group quota file */
__u32 s_overhead_clusters; /* overhead blocks/clusters in fs */
/*24c*/ __u32 s_backup_bgs[2]; /* If sparse_super2 enabled */
/*254*/ __u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
/*258*/ __u8 s_encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
/*268*/ __le32 s_lpf_ino; /* Location of the lost+found inode */
__le32 s_prj_quota_inum; /* inode for tracking project quota */
/*270*/ __le32 s_checksum_seed; /* crc32c(orig_uuid) if csum_seed set */
/*274*/ __u8 s_wtime_hi;
__u8 s_mtime_hi;
__u8 s_mkfs_time_hi;
__u8 s_lastcheck_hi;
__u8 s_first_error_time_hi;
__u8 s_last_error_time_hi;
__u8 s_first_error_errcode;
__u8 s_last_error_errcode;
/*27c*/ __le16 s_encoding; /* Filename charset encoding */
__le16 s_encoding_flags; /* Filename charset encoding flags */
__le32 s_orphan_file_inum; /* Inode for tracking orphan inodes */
__le32 s_reserved[94]; /* Padding to the end of the block */
/*3fc*/ __u32 s_checksum; /* crc32c(superblock) */
};
部分二进制对比
00:
00000400 10 00 00 00 80 00 00 00 06 00 00 00 6b 00 00 00 |............k...|
00000410 05 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................|
00000420 00 20 00 00 00 20 00 00 10 00 00 00 00 00 00 00 |. ... ..........|
00000430 10 73 89 66 00 00 ff ff 53 ef 01 00 01 00 00 00 |.s.f....S.......|
00000440 10 73 89 66 00 00 00 00 00 00 00 00 01 00 00 00 |.s.f............|
00000450 00 00 00 00 0b 00 00 00 80 00 00 00 38 00 00 00 |............8...|
f5:
00000400 10 00 00 00 80 00 00 00 06 00 00 00 16 00 00 00 |................|
00000410 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................|
00000420 00 20 00 00 00 20 00 00 10 00 00 00 13 73 89 66 |. ... .......s.f|
00000430 13 73 89 66 01 00 ff ff 53 ef 01 00 01 00 00 00 |.s.f....S.......|
00000440 10 73 89 66 00 00 00 00 00 00 00 00 01 00 00 00 |.s.f............|
00000450 00 00 00 00 0b 00 00 00 80 00 00 00 38 00 00 00 |............8...|
super block从磁盘的第1000个字节开始,前面的留给MBR什么启动分区使用
部分数据含义:
inode总数 16个
0x400 s_inodes_count = 0x10;
块总数 128个
0x404 s_blocks_count = 0x80;
保留块总数 6个
0x408 s_r_blocks_count = 0x06;
空闲块总数 从107个变成了22个(df可看)
0x40c s_free_blocks_count = 0x6b -> 0x16;
空闲inode数 刚格式化完是5个,写了5个文件就没有了
0x410 s_free_inodes_count = 0x05 -> 0x00;
每个块组拥有的块数
0x420 s_blocks_per_group = 0x2000;
inode大小 为128字节(有的环境是256字节)
0x458 s_inode_size = 0x80;
2 group desc
struct ext2_group_desc
{
__u32 bg_block_bitmap; /* Blocks bitmap block */
__u32 bg_inode_bitmap; /* Inodes bitmap block */
__u32 bg_inode_table; /* Inodes table block */
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_flags;
__u32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */
__u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
__u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
__u16 bg_itable_unused; /* Unused inodes count */
__u16 bg_checksum; /* crc16(s_uuid+group_num+group_desc)*/
};
group desc紧随super block之后
如果第一个文件系统的块大小为1024,那么group desc位于第二个块,否则位于第一个块
【解释:如果super block为1k大小,如果块大小不为1024,那就是2048或4096,这样第一个块就可以同时装下super block和group desc】
00:
00000800 03 00 00 00 04 00 00 00 05 00 00 00 6b 00 05 00 |............k...|
f5:
00000800 03 00 00 00 04 00 00 00 05 00 00 00 16 00 00 00 |................|
部分数据含义:
块位图所在的块编号 3,所以块位图在第3个块,从0xc00开始
0x800 bg_block_bitmap = 0x03;
inode位图所在的块编号 4,所以inode位图在第4个块,从0x1000开始
0x804 bg_inode_bitmap = 0x04;
inode表所在的块编号 5,所以inode位图在第5个块,从0x1400开始
0x808 bg_inode_table = 0x05;
0x80c bg_free_blocks_count = 0x69 -> 0x14;
0x80e bg_free_inodes_count = 0x05 -> 0x00;
3 block bitmap
这部分没啥好说的,就是bitmap
00:
00000c00 ff ff 0f 00 00 00 00 00 00 00 00 00 00 00 00 80 |................|
f5:
00000c00 ff ff ff ff ff ff ff ff ff 03 00 00 ff ff ff ff |................|
4 inode bitmap
一样
00:
00001000 ff 07 ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
f5:
00001000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
5 inode_table
inode的size在super block中可以查看,这里是128字节
struct ext2_inode_large {
/*00*/ __u16 i_mode; /* File mode */
__u16 i_uid; /* Low 16 bits of Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Inode Change time */
/*10*/ __u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Low 16 bits of Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
/*20*/ __u32 i_flags; /* File flags */
union {
struct {
__u32 l_i_version; /* was l_i_reserved1 */
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
} osd1; /* OS dependent 1 */
/*28*/ __u32 i_block[15];/* Pointers to blocks */
/*64*/ __u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
__u32 i_size_high;
/*70*/ __u32 i_faddr; /* Fragment address */
union {
struct {
__u16 l_i_blocks_hi;
__u16 l_i_file_acl_high;
__u16 l_i_uid_high; /* these 2 fields */
__u16 l_i_gid_high; /* were reserved2[0] */
__u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */
__u16 l_i_reserved;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
} osd2; /* OS dependent 2 */
/*80*/ __u16 i_extra_isize;
__u16 i_checksum_hi; /* crc32c(uuid+inum+inode) */
__u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
__u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */
__u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
/*90*/ __u32 i_crtime; /* File creation time */
__u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
__u32 i_version_hi; /* high 32 bits for 64-bit version */
/*9c*/ __u32 i_projid; /* Project ID */
};
inode 1
不知道干啥的,没有块指针
00001400 00 00 00 00 00 00 00 00 10 73 89 66 10 73 89 66 |.........s.f.s.f|
00001410 10 73 89 66 00 00 00 00 00 00 00 00 00 00 00 00 |.s.f............|
00001420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
inode 2
根目录
00001480 ed 41 00 00 00 04 00 00 10 73 89 66 13 73 89 66 |.A.......s.f.s.f|
00001490 13 73 89 66 00 00 00 00 00 00 03 00 02 00 00 00 |.s.f............|
000014a0 00 00 00 00 05 00 00 00 07 00 00 00 00 00 00 00 |................|
........ 00 00
i_block[15];定义了15个块指针,其中第0-11个是直接块指针,第12个是单间接块指针,第13个是双重间接块指针,第14个是三间接块指针
从addr+0x28 -> addr+0x54都是直接块,这里,根目录有一个直接块指向07(dir entry)
inode 11
lost+found
00001900 c0 41 00 00 00 30 00 00 10 73 89 66 10 73 89 66 |.A...0...s.f.s.f|
00001910 10 73 89 66 00 00 00 00 00 00 02 00 18 00 00 00 |.s.f............|
00001920 00 00 00 00 00 00 00 00 08 00 00 00 09 00 00 00 |................|
00001930 0a 00 00 00 0b 00 00 00 0c 00 00 00 0d 00 00 00 |................|
00001940 0e 00 00 00 0f 00 00 00 10 00 00 00 11 00 00 00 |................|
00001950 12 00 00 00 13 00 00 00 00 00 00 00 00 00 00 00 |................|
00001960 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001970 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
lost+found的直接块指向0x08-0x13
所以在第0x2000-0x4c00的位置
inode 12
第一个普通文件,也就是我们file1的inode(之前是全0)
00001980 a4 81 00 00 00 40 00 00 13 73 89 66 13 73 89 66 |.....@...s.f.s.f|
00001990 13 73 89 66 00 00 00 00 00 00 01 00 22 00 00 00 |.s.f........"...|
000019a0 00 00 00 00 01 00 00 00 7f 00 00 00 7d 00 00 00 |............}...|
000019b0 7e 00 00 00 15 00 00 00 16 00 00 00 17 00 00 00 |~...............|
000019c0 18 00 00 00 79 00 00 00 7a 00 00 00 7b 00 00 00 |....y...z...{...|
000019d0 7c 00 00 00 19 00 00 00 1a 00 00 00 00 00 00 00 ||...............|
000019e0 00 00 00 00 b6 00 95 b6 00 00 00 00 00 00 00 00 |................|
直接块指向0x7f 0x7d 0x7e 0x15 0x16 0x17 0x18 0x79 0x7a 0x7b 0x7c 0x19
【0x15-0x19 0x79-0x7f】
还有间接块0x1a
inode table从0x1400开始,但几个文件的inode排列在最后的原因:
前11个inode已经预留了,普通文件的inode从第12个开始
inode没有id之类的序号,查找依靠的就是在inode table里的索引
6 dir entry
存放目录和文件名的块?
00001c00 02 00 00 00 0c 00 01 02 2e 00 00 00 02 00 00 00 |................|
00001c10 0c 00 02 02 2e 2e 00 00 0b 00 00 00 14 00 0a 02 |................|
00001c20 6c 6f 73 74 2b 66 6f 75 6e 64 00 00 0c 00 00 00 |lost+found......|
00001c30 10 00 05 01 66 69 6c 65 31 00 00 00 0d 00 00 00 |....file1.......|
00001c40 10 00 05 01 66 69 6c 65 32 00 00 00 0e 00 00 00 |....file2.......|
00001c50 10 00 05 01 66 69 6c 65 33 00 00 00 0f 00 00 00 |....file3.......|
00001c60 10 00 05 01 66 69 6c 65 34 00 00 00 10 00 00 00 |....file4.......|
00001c70 94 03 05 01 66 69 6c 65 35 00 00 00 00 00 00 00 |....file5.......|
struct ext2_dir_entry_2 {
__u32 inode; /* Inode number */
__u16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length */
__u8 file_type;
char name[EXT2_NAME_LEN]; /* File name */
};
这个块里存放了很多ext2_dir_entry_2条目,他们以4字节对齐,紧凑连接(长度不固定
上述数据分析:
# 第1个是.目录
inode rec_len name_len file_type name
02 0c 01 02 2e 00 00 00
# 第2个是..目录
inode rec_len name_len file_type name
02 0c 02 02 2e 2e 00 00
# 第3个是lost+found目录
inode rec_len name_len file_type name
0b 14 0a 02 6c 6f 73 74 2b 66 6f 75 6e 64 00 00
# 第4个是文件file1
inode rec_len name_len file_type name
0c 10 05 01 66 69 6c 65 31 00 00 00
7 data区
我也是服了,不同的环境他的这个分布策略也不太一样
之前我在其他环境上测试的结果是连续的,这里就不解释了
00005400 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
...
000067f0 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
00006800 1b 00 00 00 1c 00 00 00 1d 00 00 00 1e 00 00 00 |................|
...
00006c00 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
...
00007bf0 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
00007c00 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
...
000083f0 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
00008400 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
...
00008ff0 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
00009000 25 00 00 00 26 00 00 00 27 00 00 00 28 00 00 00 |%...&...'...(...|
...
00009400 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
...
0000a3f0 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
0000a400 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 |4444444444444444|
...
0000d3f0 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 |4444444444444444|
0000d400 36 00 00 00 37 00 00 00 38 00 00 00 39 00 00 00 |6...7...8...9...|
...
0000d800 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 |4444444444444444|
...
0000e7f0 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 |4444444444444444|
0000e800 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
...
000117f0 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
00011800 47 00 00 00 48 00 00 00 49 00 00 00 4a 00 00 00 |G...H...I...J...|
...
00011c00 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
...
000117f0 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
00011800 47 00 00 00 48 00 00 00 49 00 00 00 4a 00 00 00 |G...H...I...J...|
...
00011c00 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
...
00012bf0 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 |5555555555555555|
00012c00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
...
000183f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00018400 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
...
00018bf0 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
00018c00 64 00 00 00 65 00 00 00 66 00 00 00 67 00 00 00 |d...e...f...g...|
...
00019000 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
...
00019ff0 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
0001a000 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
...
0001c3f0 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 |3333333333333333|
0001c400 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
...
0001e3f0 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
0001e400 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
...
0001fff0 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
8 间接块
需要结合前面的inode和data区中一些奇怪的数据来看
inode table部分我们已经说过,file有一个指向0x1a的单间接块
然后0x1a块的内容:
00006800 1b 00 00 00 1c 00 00 00 1d 00 00 00 1e 00 00 00 |................|
这就是单间接块,块里面充斥着32位无符号数,这里的每个数都表示着一个直接指向的块
0x1b -> 0x1e 【0x6c00 - 0x7800 + 0x400 -1】因为0x7800是最后一个块的起始地址,还要再加上块的大小
对照#7的这段数据,果然符合
00006c00 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
...
00007bf0 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 |1111111111111111|
00007c00 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 32 |2222222222222222|
双重间接块里放的就是单间接块的地址,以此类推
get_block
顺带讲一下ext2驱动里根据文件块号查找文件系统块号的逻辑:
int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
ret = ext2_get_blocks(inode, iblock, max_blocks, &bno, &new, &boundary, create);
map_bh(bh_result, inode->i_sb, bno);
bh_result->b_size = (ret << inode->i_blkbits);
static int ext2_block_to_path(struct inode *inode, long i_block, int offsets[4], int *boundary)
i_block: 文件块号,是对于一个文件而言的,比如说第0~4095字节是文件的第一个块,第4096~8191字节就是第二个文件块
bno : 文件系统块号,是对于文件系统来讲,位于设备上的第x个块
offsets[4]里面装的是0-3级间接块指针的值
ptrs_bits是用于运算的快捷值: 2^ptrs_bits = block_size / sizeof(ino), 当块大小为4096字节、块号占4字节的情景时,2^ptrs_bits = 1024, => ptrs_bits = 10
i_block & (ptrs - 1)是求余的快捷运算,等价于i_block % ptrs
1) 块号在直接块范围内: offsets[0] = i_block, 直接根据这个直接块里的值x,去读文件系统的第x个块
2) 块号在1级间接块范围内: offsets[0] = EXT2_IND_BLOCK, offsets[1] = i_block
比方说,文件系统想读文件的第200个块了,怎么在文件系统里查找这个块?
文件块号是200,ind01 = inode.i_block[EXT2_IND_BLOCK], ind01是1级间接块号,然后在文件系统里找到ind01这个块, dev[ind01]这个块里放满了块号,第i_block个单元,指向最终存放文件内容的 文件系统块号
2) 块号在2级间接块范围内: offsets[0] = EXT2_DIND_BLOCK, offsets[1] = i_block / 1024, offsets[2] = i_block % 1024
比方说,文件系统想读文件的第20000个块了,怎么在文件系统里查找这个块?
ind12 = inode.i_block[EXT2_DIND_BLOCK], ind12是2级间接块的1级间接块号, 在文件系统里找到dev[ind12]这个块,dev[ind12]里的每个bno,都指向了一个2级间接块的2级间接块ind22,每个ind22里有1024个块号
所以ind12里的每个单元,都能映射1024个文件系统块号,所以在计算偏移时这里要/1024
ind12 = 20000/1024 = 19,所以文件块号20000落在2级间接块第1级间接块的第19个单元,dev[ind12][19]指向的就是2级间接块的第2级间接块ind22
ind22中的第20000 % 1024个单元 的值就是最终的文件系统块号
9 块组
那么,块组在哪里?
其他人写的ext2文件系统博客,总是有一个很经典的图
————————————————————————————————————————————————————
| boot block | Block Group 1 | Block Group 2 | ... |
————————————————————————————————————————————————————
而如果按照我提供的制作脚本制作文件系统镜像,却并没发现第二个块组的踪影,这是为什么呢?
我也是很费解的,linux内核文档有这么一段
Block Groups
------------
Blocks are clustered into block groups in order to reduce fragmentation
and minimise the amount of head seeking when reading a large amount
of consecutive data. Information about each block group is kept in a
descriptor table stored in the block(s) immediately after the superblock.
Two blocks near the start of each group are reserved for the block usage
bitmap and the inode usage bitmap which show which blocks and inodes
are in use. Since each bitmap is limited to a single block, this means
that the maximum size of a block group is 8 times the size of a block.
this means that the maximum size of a block group is 8 times the size of a block
如果我没理解错的话,一个块组最多有8个块
那我这个划了128个块的小文件系统镜像应该有十几个块组啊?
好吧,实际上他不是这么实现的
在super block那一节,有这么一个属性:
s_blocks_per_group = 0x2000; 【每个块组拥有8192个块】
我的理解是,至少要划分超过8k个块才会出现第二个块组
但是,以每个块1024的大小计算,8k个块划出的镜像大小超过8MB,这个数据量显然不是我可以手工分析的
所以,就这样